| Bytes | Lang | Time | Link |
|---|---|---|---|
| 115 | Python 3 | 240722T014634Z | aeh5040 |
| 006 | Japt | 190119T172721Z | Shaggy |
| 050 | Ruby | 170410T125753Z | snail_ |
| 1211 | MATL | 170402T025206Z | Luis Men |
| 096 | C++ | 170410T075717Z | Toby Spe |
| 287 | Python | 170403T093142Z | PM 2Ring |
| 130 | JavaScript ES6 | 170404T082939Z | edc65 |
| 125 | Julia | 170402T032311Z | David Co |
| 031 | Mathematica | 170402T031540Z | Greg Mar |
| 040 | Scala | 170402T195343Z | corvus_1 |
| 124 | PHP | 170402T100527Z | Jör |
| 005 | Pyth | 170402T103341Z | Erik the |
| 007 | CJam | 170402T080901Z | Erik the |
| 005 | 05AB1E | 170402T075825Z | Erik the |
| 005 | 05AB1E | 170402T075743Z | Adnan |
| 005 | Jelly | 170402T075242Z | Erik the |
| 069 | Python 2 | 170402T023230Z | Dead Pos |
Python 3, 115 bytes
Recursive solution that does not generate all the permutations for "extra kudos".
def f(x,t=0,k=0,z=1):
for a in sorted({*x}):*y,=x;y.remove(a);c=t|(x[0]>a);k+=z*f(y,c);z*=c
return(x==[])*t+k
Ruby, 50 bytes
I expected this to be shorter. I wouldn't have added the sort if the docs didn't say "the implementation makes no guarantees about the order in which the permutations are yielded."
->x{x.chars.permutation.map{|v|v*""}.sort.index x}
MATL, 13 12 11 bytes
1 byte saved thanks to G B!
tY@Xu=!Af1)
Output is 1-based.
Try it online! Or verify all test cases.
Explanation
t % Input string implicitly. Duplicate
Y@ % All permutations, sorted; each in a different row
Xu % Unique rows
= % Compare for equality, with broadcast
! % Transpose
A % All: true for columns that contain all entries true
f % Find: indices of nonzero elements
1) % Get first of those indices. Implicitly display
C++, 96 bytes
We can make full use of the standard library here. The list of letters is passed as start/end iterators in standard C++ style.
#include<algorithm>
int f(char*a,char*z){int i=0;while(std::prev_permutation(a,z))++i;return i;}
We don't need to generate all permutations - since we have a transformation from one permutation to its predecessor, we simply count how many iterations it takes to reach the zeroth value.
Test program:
#include<cstring>
#include<iostream>
int main(int argc, char **argv)
{
while (*++argv)
std::cout << *argv << ": "
<< f(*argv, *argv+std::strlen(*argv)) << std::endl;
}
Test results:
BAA: 0
BAA: 1
BAA: 2
ZZZ: 0
DCBA: 23
: 0
Python, 302 287 bytes
Dead Possum has already posted a short Pythonic solution, so I decided to go for the extra kudos. This solution does not generate all of the permutations. It can rapidly calculate the permutation index of a rather large string; it also handles an empty string correctly.
from math import factorial as f
from itertools import groupby as g
def p(t,b=''):
if len(t)<2:return 0
z,b=0,b or sorted(t)
for i,c in enumerate(b):
w=b[:i]+b[i+1:]
if c==t[0]:return z+p(t[1:],w)
if i<1 or c!=b[i-1]:
n=f(len(w))
for _,v in g(w):n//=f(len(list(v)))
z+=n
Testing code:
def lexico_permute_string(s):
''' Generate all permutations of `s` in lexicographic order '''
a = sorted(s)
n = len(a) - 1
while True:
yield ''.join(a)
for j in range(n-1, -1, -1):
if a[j] < a[j + 1]:
break
else:
return
v = a[j]
for k in range(n, j, -1):
if v < a[k]:
break
a[j], a[k] = a[k], a[j]
a[j+1:] = a[j+1:][::-1]
def test_all(base):
for i, s in enumerate(lexico_permute_string(base)):
rank = p(s)
assert rank == i, (i, s, rank)
print('{:2} {} {:2}'.format(i, s, rank))
print(repr(base), 'ok\n')
for base in ('AAB', 'abbbbc'):
test_all(base)
def test(s):
print('{!r}\n{}\n'.format(s, p(s)))
for s in ('ZZZ', 'DCBA', 'a quick brown fox jumps over the lazy dog'):
test(s)
output
0 AAB 0
1 ABA 1
2 BAA 2
'AAB' ok
0 abbbbc 0
1 abbbcb 1
2 abbcbb 2
3 abcbbb 3
4 acbbbb 4
5 babbbc 5
6 babbcb 6
7 babcbb 7
8 bacbbb 8
9 bbabbc 9
10 bbabcb 10
11 bbacbb 11
12 bbbabc 12
13 bbbacb 13
14 bbbbac 14
15 bbbbca 15
16 bbbcab 16
17 bbbcba 17
18 bbcabb 18
19 bbcbab 19
20 bbcbba 20
21 bcabbb 21
22 bcbabb 22
23 bcbbab 23
24 bcbbba 24
25 cabbbb 25
26 cbabbb 26
27 cbbabb 27
28 cbbbab 28
29 cbbbba 29
'abbbbc' ok
'ZZZ'
0
'DCBA'
23
'a quick brown fox jumps over the lazy dog'
436629906477779191275460617121351796379337
Non-golfed version:
''' Determine the rank (lexicographic index) of a permutation
The permutation may contain repeated items
Written by PM 2Ring 2017.04.03
'''
from math import factorial as fac
from itertools import groupby
def lexico_permute_string(s):
''' Generate all permutations of `s` in lexicographic order '''
a = sorted(s)
n = len(a) - 1
while True:
yield ''.join(a)
for j in range(n-1, -1, -1):
if a[j] < a[j + 1]:
break
else:
return
v = a[j]
for k in range(n, j, -1):
if v < a[k]:
break
a[j], a[k] = a[k], a[j]
a[j+1:] = a[j+1:][::-1]
def perm_count(s):
''' Count the total number of permutations of sorted sequence `s` '''
n = fac(len(s))
for _, g in groupby(s):
n //= fac(sum(1 for u in g))
return n
def perm_rank(target, base):
''' Determine the permutation rank of string `target`
given the rank zero permutation string `base`,
i.e., the chars in `base` are in lexicographic order.
'''
if len(target) < 2:
return 0
total = 0
head, newtarget = target[0], target[1:]
for i, c in enumerate(base):
newbase = base[:i] + base[i+1:]
if c == head:
return total + perm_rank(newtarget, newbase)
elif i and c == base[i-1]:
continue
total += perm_count(newbase)
base = 'abcccdde'
print('total number', perm_count(base))
for i, s in enumerate(lexico_permute_string(base)):
rank = perm_rank(s, base)
assert rank == i, (i, s, rank)
#print('{:2} {} {:2}'.format(i, s, rank))
print('ok')
About lexico_permute_string
This algorithm, due to Narayana Pandita, is from https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order
To produce the next permutation in lexicographic order of sequence a
- Find the largest index j such that a[j] < a[j + 1]. If no such index exists, the permutation is the last permutation.
- Find the largest index k greater than j such that a[j] < a[k].
- Swap the value of a[j] with that of a[k].
- Reverse the sequence from a[j + 1] up to and including the final element a[n].
FWIW, you can see an annotated version of that function here.
FWIW, here's the inverse function.
def perm_unrank(rank, base, head=''):
''' Determine the permutation with given rank of the
rank zero permutation string `base`.
'''
if len(base) < 2:
return head + ''.join(base)
total = 0
for i, c in enumerate(base):
if i < 1 or c != base[i-1]:
newbase = base[:i] + base[i+1:]
newtotal = total + perm_count(newbase)
if newtotal > rank:
return perm_unrank(rank - total, newbase, head + c)
total = newtotal
# Test
target = 'a quick brown fox jumps over the lazy dog'
base = ''.join(sorted(target))
rank = perm_rank(target, base)
print(target)
print(base)
print(rank)
print(perm_unrank(rank, base))
output
a quick brown fox jumps over the lazy dog
aabcdeefghijklmnoooopqrrstuuvwxyz
436629906477779191275460617121351796379337
a quick brown fox jumps over the lazy dog
And here's a function that I wrote while developing perm_unrank which shows the breakdown of the subcounts.
def counts(base):
for i, c in enumerate(base):
newbase = base[:i] + base[i+1:]
if newbase and (i < 1 or c != base[i-1]):
yield c, perm_count(newbase)
for h, k in counts(newbase):
yield c + h, k
def show_counts(base):
TAB = ' ' * 4
for s, t in counts(base):
d = len(s) - 1
print('{}{} {}'.format(TAB * d, s, t))
# Test
base = 'abccc'
print('total number', perm_count(base))
show_counts(base)
output
a 4
ab 1
abc 1
abcc 1
ac 3
acb 1
acbc 1
acc 2
accb 1
accc 1
b 4
ba 1
bac 1
bacc 1
bc 3
bca 1
bcac 1
bcc 2
bcca 1
bccc 1
c 12
ca 3
cab 1
cabc 1
cac 2
cacb 1
cacc 1
cb 3
cba 1
cbac 1
cbc 2
cbca 1
cbcc 1
cc 6
cca 2
ccab 1
ccac 1
ccb 2
ccba 1
ccbc 1
ccc 2
ccca 1
cccb 1
JavaScript (ES6), 130 bytes
s=>(o=new Set,p=(s,q)=>s.map((t,i)=>p(t=[...s],q.concat(t.splice(i,1))))[0]||o.add(q.join``))([...s],[])&&[...o].sort().indexOf(s)
Less golfed
s=>{
o = new Set; // use a set to avoid duplicates
// recursive function to build all permutations (no cookie for me)
p = (s, q) =>
{
y = s.map( (t,i) => // execute for each char at position i
(
t = [...s], // using t as local variable, store a copy of s
x = t.splice(i,1), // remove char from t in position i, and store in array x
p(t, q.concat(x)) // recursive call
));
if (!y[0]) // if s was empty, I have a new permutation in q
o.add( q.join('')) // convert to string and add to output set
}
// call p to enumerate all permutations
p( [...s], [] ) // convert string s to array, q starts empty
o = [...o].sort() // get elements of o and sort lexicographically
return o.indexOf(s) // return the position of the input in o
}
Test
F=
s=>(o=new Set,p=(s,q)=>s.map((t,i)=>p(t=[...s],q.concat(t.splice(i,1))))[0]||o.add(q.join``))([...s],[])&&[...o].sort().indexOf(s)
function update() {
O.textContent = F(I.value)
}
update()
<input id=I value='DCBA' oninput='update()'>
<pre id=O></pre>
Julia, 121 125 bytes
Noncompeting, as it does not deal with duplicate letters correctly. I ported this from another language, from part of a solution to a Project Euler problem I did several years ago, and the first, 121-byte version had a bug because I had transposed the use of the permuted string and the sorted, canonical reference string.
f(p)=(n=0;z=length(p)-1;s=join(sort(collect(p)));for c in p z-=(n+=factorial(z)*(search(s, c)-1);p=replace(s,c,"",1);1)end;n)
For large inputs, this version uses bignums at the cost of 8 extra bytes:
f(p)=(n=0;z=BigInt(length(p)-1);s=join(sort(collect(p)));for c in p z-=(n+=factorial(z)*(search(s, c)-1);p=replace(s,c,"",1);1)end;n)
Ungolfed:
function f(perm)
nth = 0
size = length(perm) - 1
sorted = join(sort(collect(p)))
for ch in sorted
index = search(perm, ch) - 1
nth += factorial(size) * index
perm = replace(perm, ch, "" , 1) # replace at most one copy
size -= 1
end
return nth
end
Uses a factoriadic number system, q.v. Thus, it does not produce all the permutations and for large inputs will run enormously faster than those that do.
For example, the alphabet can be permuted into the rather contrived sentence "Quartz glyph job vex'd cwm finks." That sentence is the 259,985,607,122,410,643,097,474,123rd lexicographic permutation of the letters of the alphabet. (Approximately 260 septillionth permutation.) This program finds that in about 65 µs on my machine.
julia> @time f("quartzglyphjobvexdcwmfinks")
0.000065 seconds (570 allocations: 19.234 KB)
259985607122410643097474122
Note that the number ends in ...122 rather than ...123 because OP requested that the permutations be numbered from 0 rather than from 1.
Julia, 375 bytes
I've left the indentation in for readability, but the byte count is without it.
p(t,b="")=begin
l=length
if l(t)<2 return 0 end
f=factorial
g(w)=(a=[];
while w!=""
s=""
for c in w if c==w[1] s="$s$c" end end
w=replace(w,s,"")
push!(a,s)
end;a)
b=b>""?b:join(sort(collect(t)))
z=0
for(i,c) in enumerate(b)
w=b[1:i-1]*b[i+1:end]
if c==t[1] return z+p(t[2:end],w)
elseif i>1&&c==b[i-1] continue end
n=f(l(w))
for v in g(w) n=div(n,f(l(v))) end
z+=n
end
z
end
This one is just a straight Julia port of PM 2Ring's brilliant Python solution. I got hungry, so I decided I wanted the cookie after all. It's interesting to see the similarities and the differences between the two languages. I implemented itertools.groupby (in a limited form) as g(w).
But the logic isn't mine, so go and upvote PM 2Ring's answer.
Replace f=factorial with f(x)=factorial(BigInt(x)) if you want to be able to handle large inputs like p("QUARTZGLYPHJOBVEXDCWMFINKS").
Mathematica, 33 31 bytes
Changing the problem spec allowed for a 2-byte savings.
Permutations@Sort@#~Position~#&
Pure function taking a list as input and returning a nonnegative integer N in the form {{N}}.
Scala, 40 bytes
s=>s.permutations.toSeq.sorted indexOf s
To use it, assign this function to a variable:
val f:(String=>Int)=s=>s.permutations.toSeq.sorted indexOf s
println(f("BAA"))
Unfortunately, permutations returns an iterator, which doesn't have a sorted method, so it has to be converted to a Seq
PHP, 124 Bytes
$a=str_split($argn);sort($a);for($i=$j=join($a);$i<=strrev($j);$i++)$i==$argn?print+$n:(($c=count_chars)($i)!=$c($j)?:$n++);
PHP, 136 Bytes
foreach($t=($c=count_chars)($argn)as$k=>$v)$i=$s.=str_repeat(chr($k),$v);for(;$i<=strrev($s);$i++)$i==$argn?print+$n:($c($i)!=$t?:$n++);
Run with
echo '<string>' | php -nR '<code>'
Calculate with factorial
Expanded Version
function f($i){return array_product(range(1,$i));} #factorial
function p($s){
return f(strlen($s))/array_product(array_map("f",count_chars($s,1)));
} # factorial / divide through product of factorials for each char
$a=$argn;
$b="";
$r=[];
for($d=0;$a;$d++) # loop range before used chars in string
{
for($i=0;$i<strlen($a);$i++){ # loop for every char in the rest string
if($a[$i]<$a[0]) # if char is before first char order by ascii
$r[$d.$a[$i]]=p(substr_replace($a,"",$i,1)); # add range before
}
$a=substr($a,1); # remove first char
}
echo array_sum($r); # Output the range before the used permutation
Output for the string PPCG
Array
(
[0C] => 3 # in first run C is before P startposition = 3
[0G] => 3 # in first run G is before P startposition = 3+3
[1C] => 2 # in second run PC is before PP startposition = 3+3+2
[1G] => 2 # in second run PG is before PP startposition = 3+3+2+2=8
)
Python 2, 69 bytes
from itertools import*
lambda S:sorted(set(permutations(S))).index(S)