Ranking methods
You are encouraged to solve this task according to the task description, using any language you may know.
The numerical rank of competitors in a competition shows if one is better than, equal to, or worse than another based on their results in a competition.
The numerical rank of a competitor can be assigned in several different ways.
- Task
The following scores are accrued for all competitors of a competition (in best-first order):
44 Solomon 42 Jason 42 Errol 41 Garry 41 Bernard 41 Barry 39 Stephen
Create functions/methods/procedures/subroutines... that apply one, each, of the following ranking methods to an ordered list of scores with scorers:
- Standard. (Ties share what would have been their first ordinal number).
- Modified. (Ties share what would have been their last ordinal number).
- Dense. (Ties share the next available integer).
- Ordinal. ((Competitors take the next available integer. Ties are not treated otherwise).
- Fractional. (Ties share the mean of what would have been their ordinal numbers).
See the wikipedia article for a fuller description.
Show here, on this page, the ranking of the test scores under each of the numbered ranking methods.
AWK
This uses separate files for each method of ranking: <lang awk>##
- Dense ranking in file: ranking_d.awk
BEGIN{ lastresult = "!"; lastrank = 0 }
function d_rank(){
if($1==lastresult){ print lastrank, $0 }else{ lastresult = $1 print ++lastrank, $0 }
} //{d_rank() }
- Fractional ranking in file: ranking_f.awk
BEGIN{
last = "!" flen = 0 }
function f_rank(){
item = $0 if($1!=last){ if(flen){ sum = 0 for(fl=0; fl < flen;){ $0 = fifo[fl++] sum += $1 } mean = sum / flen for(fl=0; fl < flen;){ $0 = fifo[fl++] $1 = "" printf("%3g %s\n", mean, $0) } flen = 0 }} $0 = item last = $1 fifo[flen++] = sprintf("%i %s", FNR, item)
} //{f_rank()}
END{ if(flen){
sum = 0 for(fl=0; fl < flen;){ $0 = fifo[fl++] sum += $1 } mean = sum / flen for(fl=0; fl < flen;){ $0 = fifo[fl++] $1 = "" printf("%3g %s\n", mean, $0) }}}
- Modified competition ranking in file: ranking_mc.awk
BEGIN{
lastresult = "!" flen = 0 }
function mc_rank(){
if($1==lastresult){ fifo[flen++] = $0 }else{ for(fl=0; fl < flen;){ print FNR-1, fifo[fl++]} flen = 0 fifo[flen++] = $0 lastresult = $1}
} //{mc_rank()}
END{ for(fl=0; fl < flen;){
print FNR, fifo[fl++]} }
- Ordinal ranking in file: ranking_o.awk
function o_rank(){ print FNR, $0 } //{o_rank() }
- Standard competition ranking in file: ranking_sc.awk
BEGIN{ lastresult = lastrank = "!" }
function sc_rank(){
if($1==lastresult){ print lastrank, $0 }else{ print FNR, $0 lastresult = $1 lastrank = FNR}
} //{sc_rank()} </lang>
The input as a file ranking.txt
:
44 Solomon 42 Jason 42 Errol 41 Garry 41 Bernard 41 Barry 39 Stephen
- Output:
C:\Users\RC\Code>awk -f ranking_sc.awk ranking.txt 1 44 Solomon 2 42 Jason 2 42 Errol 4 41 Garry 4 41 Bernard 4 41 Barry 7 39 Stephen C:\Users\RC\Code>awk -f ranking_mc.awk ranking.txt 1 44 Solomon 3 42 Jason 3 42 Errol 6 41 Garry 6 41 Bernard 6 41 Barry 7 39 Stephen C:\Users\RC\Code>awk -f ranking_d.awk ranking.txt 1 44 Solomon 2 42 Jason 2 42 Errol 3 41 Garry 3 41 Bernard 3 41 Barry 4 39 Stephen C:\Users\RC\Code>awk -f ranking_o.awk ranking.txt 1 44 Solomon 2 42 Jason 3 42 Errol 4 41 Garry 5 41 Bernard 6 41 Barry 7 39 Stephen C:\Users\RC\Code>awk -f ranking_f.awk ranking.txt 1 44 Solomon 2.5 42 Jason 2.5 42 Errol 5 41 Garry 5 41 Bernard 5 41 Barry 7 39 Stephen C:\Users\RC\Code>
Go
<lang go>package main
import ( "fmt" "sort" )
type rankable interface { Len() int RankEqual(int, int) bool }
func StandardRank(d rankable) []float64 { r := make([]float64, d.Len()) var k int for i := range r { if i == 0 || !d.RankEqual(i, i-1) { k = i + 1 } r[i] = float64(k) } return r }
func ModifiedRank(d rankable) []float64 { r := make([]float64, d.Len()) for i := range r { k := i + 1 for j := i + 1; j < len(r) && d.RankEqual(i, j); j++ { k = j + 1 } r[i] = float64(k) } return r }
func DenseRank(d rankable) []float64 { r := make([]float64, d.Len()) var k int for i := range r { if i == 0 || !d.RankEqual(i, i-1) { k++ } r[i] = float64(k) } return r }
func OrdinalRank(d rankable) []float64 { r := make([]float64, d.Len()) for i := range r { r[i] = float64(i + 1) } return r }
func FractionalRank(d rankable) []float64 { r := make([]float64, d.Len()) for i := 0; i < len(r); { var j int f := float64(i + 1) for j = i + 1; j < len(r) && d.RankEqual(i, j); j++ { f += float64(j + 1) } f /= float64(j - i) for ; i < j; i++ { r[i] = f } } return r }
type scores []struct { score int name string }
func (s scores) Len() int { return len(s) } func (s scores) RankEqual(i, j int) bool { return s[i].score == s[j].score } func (s scores) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s scores) Less(i, j int) bool { if s[i].score != s[j].score { return s[i].score > s[j].score } return s[i].name < s[j].name }
var data = scores{ {44, "Solomon"}, {42, "Jason"}, {42, "Errol"}, {41, "Garry"}, {41, "Bernard"}, {41, "Barry"}, {39, "Stephen"}, }
func main() { show := func(name string, fn func(rankable) []float64) { fmt.Println(name, "Ranking:") r := fn(data) for i, d := range data { fmt.Printf("%4v - %2d %s\n", r[i], d.score, d.name) } }
sort.Sort(data) show("Standard", StandardRank) show("\nModified", ModifiedRank) show("\nDense", DenseRank) show("\nOrdinal", OrdinalRank) show("\nFractional", FractionalRank) }</lang>
- Output:
Standard Ranking: 1 - 44 Solomon 2 - 42 Errol 2 - 42 Jason 4 - 41 Barry 4 - 41 Bernard 4 - 41 Garry 7 - 39 Stephen Modified Ranking: 1 - 44 Solomon 3 - 42 Errol 3 - 42 Jason 6 - 41 Barry 6 - 41 Bernard 6 - 41 Garry 7 - 39 Stephen Dense Ranking: 1 - 44 Solomon 2 - 42 Errol 2 - 42 Jason 3 - 41 Barry 3 - 41 Bernard 3 - 41 Garry 4 - 39 Stephen Ordinal Ranking: 1 - 44 Solomon 2 - 42 Errol 3 - 42 Jason 4 - 41 Barry 5 - 41 Bernard 6 - 41 Garry 7 - 39 Stephen Fractional Ranking: 1 - 44 Solomon 2.5 - 42 Errol 2.5 - 42 Jason 5 - 41 Barry 5 - 41 Bernard 5 - 41 Garry 7 - 39 Stephen
J
Implementation:
<lang J>competitors=:<;._1;._2]0 :0
44 Solomon 42 Jason 42 Errol 41 Garry 41 Bernard 41 Barry 39 Stephen
)
scores=: {."1
standard=: 1+i.~ modified=: 1+i:~ dense=: #/.~ # #\@~. ordinal=: #\ fractional=: #/.~ # ] (+/%#)/. #\
rank=:1 :'<"0@u@:scores,.]'</lang>
Note that we assume that the competitors are already in the right order. Also, of course (as is common when using J) we use the J command line, because that is portable across operating systems (for example: the OS command line is difficult to use on phones).
Task examples:
<lang J> standard rank competitors ┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │2│42│Errol │ ├─┼──┼───────┤ │4│41│Garry │ ├─┼──┼───────┤ │4│41│Bernard│ ├─┼──┼───────┤ │4│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘
modified rank competitors
┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │3│42│Jason │ ├─┼──┼───────┤ │3│42│Errol │ ├─┼──┼───────┤ │6│41│Garry │ ├─┼──┼───────┤ │6│41│Bernard│ ├─┼──┼───────┤ │6│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘
dense rank competitors
┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │2│42│Errol │ ├─┼──┼───────┤ │3│41│Garry │ ├─┼──┼───────┤ │3│41│Bernard│ ├─┼──┼───────┤ │3│41│Barry │ ├─┼──┼───────┤ │4│39│Stephen│ └─┴──┴───────┘
ordinal rank competitors
┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │3│42│Errol │ ├─┼──┼───────┤ │4│41│Garry │ ├─┼──┼───────┤ │5│41│Bernard│ ├─┼──┼───────┤ │6│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘
fractional rank competitors
┌───┬──┬───────┐ │1 │44│Solomon│ ├───┼──┼───────┤ │2.5│42│Jason │ ├───┼──┼───────┤ │2.5│42│Errol │ ├───┼──┼───────┤ │5 │41│Garry │ ├───┼──┼───────┤ │5 │41│Bernard│ ├───┼──┼───────┤ │5 │41│Barry │ ├───┼──┼───────┤ │7 │39│Stephen│ └───┴──┴───────┘</lang>
Java
<lang java>import java.util.*;
public class RankingMethods {
final static String[] input = {"44 Solomon", "42 Jason", "42 Errol", "41 Garry", "41 Bernard", "41 Barry", "39 Stephen"};
public static void main(String[] args) { int len = input.length;
Map<String, int[]> map = new TreeMap<>((a, b) -> b.compareTo(a)); for (int i = 0; i < len; i++) { String key = input[i].split("\\s+")[0]; int[] arr; if ((arr = map.get(key)) == null) arr = new int[]{i, 0}; arr[1]++; map.put(key, arr); } int[][] groups = map.values().toArray(new int[map.size()][]);
standardRanking(len, groups); modifiedRanking(len, groups); denseRanking(len, groups); ordinalRanking(len); fractionalRanking(len, groups); }
private static void standardRanking(int len, int[][] groups) { System.out.println("\nStandard ranking"); for (int i = 0, rank = 0, group = 0; i < len; i++) { if (group < groups.length && i == groups[group][0]) { rank = i + 1; group++; } System.out.printf("%d %s%n", rank, input[i]); } }
private static void modifiedRanking(int len, int[][] groups) { System.out.println("\nModified ranking"); for (int i = 0, rank = 0, group = 0; i < len; i++) { if (group < groups.length && i == groups[group][0]) rank += groups[group++][1]; System.out.printf("%d %s%n", rank, input[i]); } }
private static void denseRanking(int len, int[][] groups) { System.out.println("\nDense ranking"); for (int i = 0, rank = 0; i < len; i++) { if (rank < groups.length && i == groups[rank][0]) rank++; System.out.printf("%d %s%n", rank, input[i]); } }
private static void ordinalRanking(int len) { System.out.println("\nOrdinal ranking"); for (int i = 0; i < len; i++) System.out.printf("%d %s%n", i + 1, input[i]); }
private static void fractionalRanking(int len, int[][] groups) { System.out.println("\nFractional ranking"); float rank = 0; for (int i = 0, tmp = 0, group = 0; i < len; i++) { if (group < groups.length && i == groups[group][0]) { tmp += groups[group++][1]; rank = (i + 1 + tmp) / 2.0F; } System.out.printf("%2.1f %s%n", rank, input[i]); } }
}</lang>
Standard ranking 1 44 Solomon 2 42 Jason 2 42 Errol 4 41 Garry 4 41 Bernard 4 41 Barry 7 39 Stephen Modified ranking 1 44 Solomon 3 42 Jason 3 42 Errol 6 41 Garry 6 41 Bernard 6 41 Barry 7 39 Stephen Dense ranking 1 44 Solomon 2 42 Jason 2 42 Errol 3 41 Garry 3 41 Bernard 3 41 Barry 4 39 Stephen Ordinal ranking 1 44 Solomon 2 42 Jason 3 42 Errol 4 41 Garry 5 41 Bernard 6 41 Barry 7 39 Stephen Fractional ranking 1,0 44 Solomon 2,5 42 Jason 2,5 42 Errol 5,0 41 Garry 5,0 41 Bernard 5,0 41 Barry 7,0 39 Stephen
Perl 6
<lang perl6>my @scores =
Solomon => 44, Jason => 42, Errol => 42, Garry => 41, Bernard => 41, Barry => 41, Stephen => 39;
sub tiers (@s) { @s.classify(*.value).pairs.sort.reverse.map: { [.value».key] } }
sub standard (@s) {
my $rank = 1; gather for tiers @s -> @players {
take $rank => @players; $rank += @players;
}
}
sub modified (@s) {
my $rank = 0; gather for tiers @s -> @players {
$rank += @players; take $rank => @players;
}
}
sub dense (@s) { tiers(@s).map: { ++$_ => @^players } }
sub ordinal (@s) { @s.map: ++$_ => *.key }
sub fractional (@s) {
my $rank = 1; gather for tiers @s -> @players {
my $beg = $rank; my $end = $rank += @players; take [+]($beg ..^ $end) / @players => @players;
}
}
say "Standard:"; .say for standard @scores; say "\nModified:"; .say for modified @scores; say "\nDense:"; .say for dense @scores; say "\nOrdinal:"; .say for ordinal @scores; say "\nFractional:"; .say for fractional @scores;</lang>
- Output:
Standard: 1 => ["Solomon"] 2 => ["Jason", "Errol"] 4 => ["Garry", "Bernard", "Barry"] 7 => ["Stephen"] Modified: 1 => ["Solomon"] 3 => ["Jason", "Errol"] 6 => ["Garry", "Bernard", "Barry"] 7 => ["Stephen"] Dense: 1 => ["Solomon"] 2 => ["Jason", "Errol"] 3 => ["Garry", "Bernard", "Barry"] 4 => ["Stephen"] Ordinal: 1 => "Solomon" 2 => "Jason" 3 => "Errol" 4 => "Garry" 5 => "Bernard" 6 => "Barry" 7 => "Stephen" Fractional: 1.0 => ["Solomon"] 2.5 => ["Jason", "Errol"] 5.0 => ["Garry", "Bernard", "Barry"] 7.0 => ["Stephen"]
Python
<lang python>def mc_rank(iterable, start=1):
"""Modified competition ranking""" lastresult, fifo = None, [] for n, item in enumerate(iterable, start-1): if item[0] == lastresult: fifo += [item] else: while fifo: yield n, fifo.pop(0) lastresult, fifo = item[0], fifo + [item] while fifo: yield n+1, fifo.pop(0)
def sc_rank(iterable, start=1):
"""Standard competition ranking""" lastresult, lastrank = None, None for n, item in enumerate(iterable, start): if item[0] == lastresult: yield lastrank, item else: yield n, item lastresult, lastrank = item[0], n
def d_rank(iterable, start=1):
"""Dense ranking""" lastresult, lastrank = None, start - 1, for item in iterable: if item[0] == lastresult: yield lastrank, item else: lastresult, lastrank = item[0], lastrank + 1 yield lastrank, item
def o_rank(iterable, start=1):
"""Ordinal ranking""" yield from enumerate(iterable, start)
def f_rank(iterable, start=1):
"""Fractional ranking""" last, fifo = None, [] for n, item in enumerate(iterable, start): if item[0] != last: if fifo: mean = sum(f[0] for f in fifo) / len(fifo) while fifo: yield mean, fifo.pop(0)[1] last = item[0] fifo.append((n, item)) if fifo: mean = sum(f[0] for f in fifo) / len(fifo) while fifo: yield mean, fifo.pop(0)[1]
if __name__ == '__main__':
scores = [(44, 'Solomon'), (42, 'Jason'), (42, 'Errol'), (41, 'Garry'), (41, 'Bernard'), (41, 'Barry'), (39, 'Stephen')]
print('\nScores to be ranked (best first):') for s in scores: print(' %2i %s' % (s )) for ranker in [sc_rank, mc_rank, d_rank, o_rank, f_rank]: print('\n%s:' % ranker.__doc__) for rank, score in ranker(scores): print(' %3g, %r' % (rank, score))</lang>
- Output:
Scores to be ranked (best first): 44 Solomon 42 Jason 42 Errol 41 Garry 41 Bernard 41 Barry 39 Stephen Standard competition ranking: 1, (44, 'Solomon') 2, (42, 'Jason') 2, (42, 'Errol') 4, (41, 'Garry') 4, (41, 'Bernard') 4, (41, 'Barry') 7, (39, 'Stephen') Modified competition ranking: 1, (44, 'Solomon') 3, (42, 'Jason') 3, (42, 'Errol') 6, (41, 'Garry') 6, (41, 'Bernard') 6, (41, 'Barry') 7, (39, 'Stephen') Dense ranking: 1, (44, 'Solomon') 2, (42, 'Jason') 2, (42, 'Errol') 3, (41, 'Garry') 3, (41, 'Bernard') 3, (41, 'Barry') 4, (39, 'Stephen') Ordinal ranking: 1, (44, 'Solomon') 2, (42, 'Jason') 3, (42, 'Errol') 4, (41, 'Garry') 5, (41, 'Bernard') 6, (41, 'Barry') 7, (39, 'Stephen') Fractional ranking: 1, (44, 'Solomon') 2.5, (42, 'Jason') 2.5, (42, 'Errol') 5, (41, 'Garry') 5, (41, 'Bernard') 5, (41, 'Barry') 7, (39, 'Stephen')
Ruby
<lang ruby>ar = "44 Solomon 42 Jason 42 Errol 41 Garry 41 Bernard 41 Barry 39 Stephen".lines.map{|line| line.split}
grouped = ar.group_by{|pair| pair.delete_at(0).to_i} s_rnk = 1 m_rnk = o_rnk = f_rnk = 0 puts "stand.\tmod.\tdense\tord.\tfract."
grouped.each.with_index do |(score, names), i|
d_rnk = i + 1 m_rnk += names.flatten!.size f_rnk = (s_rnk + m_rnk)/2.0 names.each do |name| o_rnk += 1 puts "#{s_rnk}\t#{m_rnk}\t#{d_rnk}\t#{o_rnk}\t#{f_rnk.to_s.gsub(".0","")}\t#{score} #{name}" end s_rnk += names.size
end</lang>
- Output:
stand. mod. dense ord. fract. 1 1 1 1 1 44 Solomon 2 3 2 2 2.5 42 Jason 2 3 2 3 2.5 42 Errol 4 6 3 4 5 41 Garry 4 6 3 5 5 41 Bernard 4 6 3 6 5 41 Barry 7 7 4 7 7 39 Stephen