Sorting with a modulus - php

I am trying trying to sort a list into columns with uksort.
The array already alpha sorted, so it is like array('A','B','C','D','E','F','G','H','I','J','K','L','M')
Which gets displayed in html, as floated elements:
A B C D
E F G H
I J K L
M
I want it reordered so it displays like this:
A E H K
B F I L
C G J M
D
So the sorted array would be: array('A','E','H','K','B','F','I','L','C','G','J','M','D'
Basically, the same as Sorting a list alphabetically with a modulus but for php. I've tried taking the solution for javascript and convert it into php, but I'm not getting something right. Anyone have any ideas of how to do this in php?
This is what I have tried:
function cmp_nav_by4($a, $b) {
if (($a % 5) < ($b % 5)) {
return 1;
} elseif (($a % 4) > ($b % 4)) {
return -1;
} else {
return $a < $b ? 1 : -1;
}
}
$result = uksort($thearray, "cmp_nav_by4");

Setting up the following:
$array = range('A', 'M');
$columns = 4;
$length = count($array);
print_matrix($array, $columns);
Which outputs each member and it's key by index (row and colum) and as well the elements order on top:
One row - A B C D E F G H I J K L M
A[ 0] B[ 1] C[ 2] D[ 3]
E[ 4] F[ 5] G[ 6] H[ 7]
I[ 8] J[ 9] K[10] L[11]
M[12]
The javascript code linked could be easily converted to PHP. However, if you look closely to that question/answer, it becomes clear that it only work with full rows, like with my previous attempt:
function callback_sort($array, $columns)
{
$sort = function($columns)
{
return function($a, $b) use ($columns)
{
$bycol = ($a % $columns) - ($b % $columns);
return $bycol ? : $a - $b;
};
};
uksort($array, $sort(4));
return $array;
}
Output:
One row - A E I M B F J C G K D H L
A[ 0] E[ 4] I[ 8] M[12]
B[ 1] F[ 5] J[ 9] C[ 2]
G[ 6] K[10] D[ 3] H[ 7]
L[11]
So it's just that the function provided in the other question does not work.
But as the array is already sorted, you don't need to sort it again but just to change the order or elements. But which order? If the matrix is not complete e.g. n x n fully filled, per each column, a different new index needs to be calculated. Taken the example with 13 elements (A-M) gives you the following distribution of rows per column:
column: 1 2 3 4
rows: 4 3 3 3
So per each column, the value differs. For example at index 12, the 13th element is in the 4th row. On the way coming to that position, it has been passed 4 times through column 1 and 3 times in the other columns 2-4. So to get the virtual index of the iterated index, you need so sum how often you've been in each column to find out how many numbers in the original index you were going forward. If you go over the maximum number of members, you continue over at 0.
So this could be iteratively solved by stepping forward per each index to distribute the calculation over the indexes:
Index 0:
No column: 0
Index 1:
1x in column is which has 4 rows: 4
Index 2:
1x in column 1 (4 rows) and 1x in other columns (3 rows): 4 + 3
... and so on. If the virtual index goes over 12, it will start at 0, for example for the 5th Element (index 4) the virtual index would calculate 13:
Index 4:
1x 4 rows and 3x 3 rows = 13 (4 + 9)
13 > 12 => 1 (13 - 12)
Now filling a new array by starting with the virtual index 0 and giving the appropriate offset each time (look in which column you are, add the number of rows of that column, wrap around if necessary) will give the desired output:
One row - A E H K B F I L C G J M D
A[ 0] E[ 4] H[ 7] K[10]
B[ 1] F[ 5] I[ 8] L[11]
C[ 2] G[ 6] J[ 9] M[12]
D[ 3]
Written in code, that's a simple foreach over the original indexes. By maintaining an index of keys as well, this works with any array, even those with string keys:
$floor = floor($length/$columns);
$modulo = $length % $columns;
$max = $length-1;
$virtual = 0;
$keys = array_keys($array);
$build = array();
foreach($keys as $index => $key)
{
$vkey = $keys[$virtual];
$build[$vkey] = $array[$vkey];
$virtual += $floor + ($index % $columns < $modulo);
($virtual>$max) && $virtual %= $max;
}
print_matrix($build, $columns);
And that's it: Demo, Gist.

#hakre has the correct code answer. The why:
The underlying sort function, Zend_qsort, does not actually reorder the elements and keys. Instead, it reorders the internal array buckets the zend engine uses. If you ksort a numerically indexed array, then iterate over with $q = count($array);for($i=0; $i<$q); $i++) it will return the values exactly as before; if you iterate with for($key in $array) you will get they new key ordering.

Related

How to generate ABBAABBA... sequence with PHP and CSS nth-child

I have a two column layout with this disposition of elements:
A B
B A
A B
B A
…
A and B elements have content from different origins and also different styles. So, I'm trying to find an expression to generate this A B B A A B B A … sequence both in PHP and CSS nth-child.
This is what I'm doing to generate the layout:
/* $post_ids is an Array of ID's */
$count = count( $post_ids );
for ( $i = 0; $i < $count; $i++ ) {
$classes = ['teaser', 'lg-6', 'md-6', 'sm-12'];
if ( ( 2 * $i ) % 2 == 0 ) { // This is wrong. Always true!
$classes[] = 'a-element';
} else {
$classes[] = 'b-element';
}
insert_post( $post_ids[ $i ], $classes ); // This is a custom function
}
And this is my CSS:
.teaser:nth-child(2n) { // Also wrong
/* Styles for A items */
}
I know once I got the correct PHP sequence I could replace my CSS with:
.a-element {…}
.b-element {…}
But I'd like to know if this could be also done with nth-child...
I guess it can't be that hard, but I'm kinda stuck with this... Any hint or help will be much appreciated!
Edit: After #axiac's answer and some research, I've learned that nth-child only allows:
a number - any positive integer (1,2,3,20, etc.)
a keyword - even or odd
an expression - in the form of an+b (a, b being integers)
So, I guess what I want can't be done with CSS's nth-child. Thank you guys!
The PHP code you need is:
if ((int)(($i + 1) / 2) % 2 == 0 ) {
$classes[] = 'a-element';
} else {
$classes[] = 'b-element';
}
Update
At OP's request, this is how I produced the code above. The desired outcome is:
A B B A A B B A ..
after the initial A each symbol repeats twice. We use a for (;;) loop to iterate from 0 to some $n greater than zero;
the alternation is provided by the modulo operation (% 2);
grouping the values can be done by finding a common property for consecutive numbers. The integer result of division by the desired group size is such a property. If you need to output 5 As followed by 5 Bs then you just notice that any positive integer number can be written as 5 * k + r where r is one of 0, 1, 2, 3, 4 (this is how the division of integer numbers works). There are exactly 5 consecutive integer numbers that divided by 5 produce the same integer result (and different remainders);
given that the PHP division always produce real numbers, the conversion of the result to int (by type casting the result to (int)) is needed;
the + 1 offset is needed to "push" one A before the first 2 Bs. 0 and 1 produce the same result (0) when divided by 2 (they make the first group), 2 and 3 produce 1 and make the second group and so on.
Generalization
If you need to produce N different types of blocks (A, B, C, D a.s.o.), each of them appear M consecutive times (A A A B B B C C C D D D ..., M is 3 here) then the formula is:
(int)(($i + $k) / M) % N
The value produced by this formula is one of 0, 1 ... N - 1 and it tells what symbol to use (A, B a.s.o.). Without + $k this formula generates M instances of A followed by M instances of B, M instances of C and so on until the last symbol. It prints M * N symbols in total then it starts over with A.
The value of $k is one of 0 .. M * N - 1 and it allows the sequence to start from any point inside the sequence. It represents the number of symbols to skip from the start of the sequence.
If I understand correctly the problem is with:
(2 * $i) % 2 == 0
this will return true for all iterations (2 * $i) is always even
try:
$i % 2 == 0
instead

Reading array diagonally for transposition cipher?

What I'm doing here is probably stupid and useless, but it turned out to be a great way to learn php, I appreciate your help.
I came up with a cipher which wraps text in a spiral much like Ulam's spiral maps numbers.
Refer to this image:
I consider the whole spiral as a cube, so if the string is too short to form a full cube, the remaining characters are left as empty spaces (in my picture example there is one space left, because the string is 24 characters and the next full cube is at 25 characters.
I want to add a further step of obfuscation, reading the array diagonally, so the output would be like this:
What is an easy/efficient way to achieve this? I'm storing the data in a 2D array, so it looks like this:
field[0][0]='l';
Bonus tangent question: How easily would something like this be deciphered?
Thank you!
This is the schema for a 5x5 square:
yx yx yx yx yx
0 1 2 3 4 a > 00
0 a b c d e b > 10 01
1 b c d e f c > 20 11 02
2 c d e f g d > 30 21 12 03
3 d e f g h e > 40 31 22 13 04
4 e f g h i f > 41 32 23 14
g > 42 33 24
h > 43 34
i > 44
As you can see, we have:
9 groups (= cols+rows-1 or keys+keys+1);
in each line, the y+x sum is the same, increasing from 0 to 8;
in each line, the y decreases, while the x increases.
each line ends when x reaches initial y
Based on these assumptions, we can write a single loop, using $sum and $startY as criteria to change line: when the decreasing $x has same value of $startY, we increment $sum and we set next $startY to the lowest value between $sum and the higher $array key, then we set next $x to the difference between $sum and $y:
$sum = $startY = $y = $x = 0;
while( $sum < 2*count($array)-1 )
{
echo $array[$y][$x];
if( $x == $startY )
{
$sum++;
$startY = $y = min( $sum, count($array)-1 );
$x = $sum - $y;
}
else
{
$y--;
$x++;
}
}
The result for above square is:
abbcccddddeeeeeffffggghhi
Looking at this eval.in demo you can see three different examples.
Just use for loops. It doesn't matter what language you are learning, you need to learn how to use for loops (or while loops or foreach loops or any good control structure).
You are going 00, then 10, 01, then 20, 11, 02, then 30, 21, 12, 03, etc... YOu can see that the first number decreases by 1 and the second number increases by 1. That goes until you hit n0...0n. That covers the first half of the square...
// Assume $n is the width/height of the square
for($m=0; $m<=$n; $m++)
{
for($a=$n; $a>=0; $a--)
{
for($b=0; $b<=$n; $b++)
{
//Do whatever you want with $array[$a][$n]...
}
}
}
Now, the second half of the square hits 41, 32, 23, 14 on the first stripe. It hits 42, 33, 24 on the second stripe. It hits 43, 34, and finally 44. You can see they both step up until they hit $n
for($m=1; $m<=$n; $m++)
{
for($b=$m; $b<=$n; $b++) // Put B on the outside because it is the limitation
{
for($a=4; $b<=$n; $a--)
{
//Do what you want with $a and $b
}
}
}
Now... can this be deciphered easily? Yes. You are just scrambling up the letters. No matter how you scramble it, it is deciphered easily. You need to substitute the letters with a replacement set that changes. Optimally, you want a completely new replacement set per letter you replace - which is difficult to use. So, most ciphers use a set of replacement sets, say 32 sets of replacement letters or symbols, that cycle through as randomly as possible.

Write a program to get N number of positive non-zero integers, so that the sum and product of these numbers are equal

Let's say, the numbers are A, B, C, D and E.
So, what I want is-
A + B + C + D + E = A * B * C * D * E
I want to write a program (preferably PHP) that solves it.
Note: The numbers can be repeated. I mean, we can have A, B, B, D and E for example.
Yes. I solved it myself.
The answer is number of numbers, 2 and (number of numbers - 2) of 1s!
Say, if we consider 5 numbers, they'll be 5, 2, 1, 1 and 1.
If we consider 6 numbers, they'll be 6, 2, 1, 1, 1 and 1.
Here is php program-
<?php
$n = 5;
$numbers = array();
$numbers[] = $n;
$numbers[] = 2;
for($i=1; $i<=($n-2); $i++){
$numbers[] = 1;
}
$numbers array will contain the numbers!

How to sort an array by value in a certain order?

Given an array of any size (from 1 to 4 rounds) with ranks numbering from 1 to 8 (or more), how can I take that array and sort it bracket style, so rank 1 is first, rank 2 is last, then rank 8 is next, then rank 7 is second to last... like
Then the next round ..
1, 4, 3, 2
I am trying to sort tournament brackets but not having much luck when it comes to sorting the ranking, and also in a way that scales well so the display does not break.
Edit:
Some clarification, each bracket size needs to break down like so:
If the bracket has 8 games, the game numbers are 1 through 8, so that round needs to arrange itself like:
Game 1
Game 8
Game 5
Game 4
Game 6
Game 3
Game 7
Game 2
So then, on the next round, it has 4 games, which would come out as:
Game 1
Game 4
Game 3
Game 2
And so on:
Game 1
Game 2
Finally,
Game 1
It also needs to work if the starting bracket had 16 games instead of 8, or 32, or more. The idea is that the winner of Game 1 and Game 8 play each other in Game 1 on the next round. The first game and second game are always the first and last on each bracket. Then it works it's way inward.
This isn't sorting the list. Unless you really need to sort the list, indices may be faster and more efficient.
The match ups will be set up like (current_rank), (total ranks) - (current_rank) + 1
Since there are 8 ranks,
1, 8 -1 +1 = 8
2, 8 -2 +1 = 7
3, 8 -3 +1 = 6
4, 8 -4 +1 = 5
So the code would look something like
<?php
$rankscount = count($ranks);
for ($i = 1; $i <= $rankscount / 2; $i++) {
echo "matchup will be: rank " . $i . " , rank " . $rankscount - $i + 1;
}
?>
After each round, reseed the function with the new sorted list, and you'll get 1vs4. 2vs3.
I'm not a professional at PHP, but hopefully this helps.
The following function sorts an array of ['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8'] into an order of ['r1', 'r8', 'r2', 'r7', 'r3', 'r6', 'r4', 'r5'].
An array of ['r1', 'r2', 'r3', 'r4'] will be rearranged into ['r1', 'r4', 'r2', 'r3']
function rearranged($array) {
sort($array);
$result = array();
$length = count($array);
$offset = 0;
// Handling two elements at once, therefore just do $lenght/2 iterations
for ($i = 0; $i < $length/2; $i++) {
// $i + $offset: The current element in the original array
// + the offset of fields already filled in the results array
$result[$i + $offset] = $array[$i];
// $i + 1 + $offset: The next element in the results array
$result[$i + 1 + $offset] = $array[$length - $i -1];
// Increment offset
$offset++;
}
return $result;
}
I am not using any inbuilt sort function since they compare all keys to each others, assuming that your array already is in order just iterating and swapping positions should be much faster. If the keys are not ordered you can call a inbuilt sort function such as sort(sorts by value) or ksort (sorts by key).
To note is as well, that this function only works properly for arrays with an even amount of elements. If the number of elements is uneven the last element will be dropped from the results array.

Finding n-th permutation without computing others

Given an array of N elements representing the permutation atoms, is there an algorithm like that:
function getNthPermutation( $atoms, $permutation_index, $size )
where $atoms is the array of elements, $permutation_index is the index of the permutation and $size is the size of the permutation.
For instance:
$atoms = array( 'A', 'B', 'C' );
// getting third permutation of 2 elements
$perm = getNthPermutation( $atoms, 3, 2 );
echo implode( ', ', $perm )."\n";
Would print:
B, A
Without computing every permutation until $permutation_index ?
I heard something about factoradic permutations, but every implementation i've found gives as result a permutation with the same size of V, which is not my case.
Thanks.
As stated by RickyBobby, when considering the lexicographical order of permutations, you should use the factorial decomposition at your advantage.
From a practical point of view, this is how I see it:
Perform a sort of Euclidian division, except you do it with factorial numbers, starting with (n-1)!, (n-2)!, and so on.
Keep the quotients in an array. The i-th quotient should be a number between 0 and n-i-1 inclusive, where i goes from 0 to n-1.
This array is your permutation. The problem is that each quotient does not care for previous values, so you need to adjust them. More explicitly, you need to increment every value as many times as there are previous values that are lower or equal.
The following C code should give you an idea of how this works (n is the number of entries, and i is the index of the permutation):
/**
* #param n The number of entries
* #param i The index of the permutation
*/
void ithPermutation(const int n, int i)
{
int j, k = 0;
int *fact = (int *)calloc(n, sizeof(int));
int *perm = (int *)calloc(n, sizeof(int));
// compute factorial numbers
fact[k] = 1;
while (++k < n)
fact[k] = fact[k - 1] * k;
// compute factorial code
for (k = 0; k < n; ++k)
{
perm[k] = i / fact[n - 1 - k];
i = i % fact[n - 1 - k];
}
// readjust values to obtain the permutation
// start from the end and check if preceding values are lower
for (k = n - 1; k > 0; --k)
for (j = k - 1; j >= 0; --j)
if (perm[j] <= perm[k])
perm[k]++;
// print permutation
for (k = 0; k < n; ++k)
printf("%d ", perm[k]);
printf("\n");
free(fact);
free(perm);
}
For example, ithPermutation(10, 3628799) prints, as expected, the last permutation of ten elements:
9 8 7 6 5 4 3 2 1 0
Here's a solution that allows to select the size of the permutation. For example, apart from being able to generate all permutations of 10 elements, it can generate permutations of pairs among 10 elements. Also it permutes lists of arbitrary objects, not just integers.
function nth_permutation($atoms, $index, $size) {
for ($i = 0; $i < $size; $i++) {
$item = $index % count($atoms);
$index = floor($index / count($atoms));
$result[] = $atoms[$item];
array_splice($atoms, $item, 1);
}
return $result;
}
Usage example:
for ($i = 0; $i < 6; $i++) {
print_r(nth_permutation(['A', 'B', 'C'], $i, 2));
}
// => AB, BA, CA, AC, BC, CB
How does it work?
There's a very interesting idea behind it. Let's take the list A, B, C, D. We can construct a permutation by drawing elements from it like from a deck of cards. Initially we can draw one of the four elements. Then one of the three remaining elements, and so on, until finally we have nothing left.
Here is one possible sequence of choices. Starting from the top we're taking the third path, then the first, the the second, and finally the first. And that's our permutation #13.
Think about how, given this sequence of choices, you would get to the number thirteen algorithmically. Then reverse your algorithm, and that's how you can reconstruct the sequence from an integer.
Let's try to find a general scheme for packing a sequence of choices into an integer without redundancy, and unpacking it back.
One interesting scheme is called decimal number system. "27" can be thought of as choosing path #2 out of 10, and then choosing path #7 out of 10.
But each digit can only encode choices from 10 alternatives. Other systems that have a fixed radix, like binary and hexadecimal, also can only encode sequences of choices from a fixed number of alternatives. We want a system with a variable radix, kind of like time units, "14:05:29" is hour 14 from 24, minute 5 from 60, second 29 from 60.
What if we take generic number-to-string and string-to-number functions, and fool them into using mixed radixes? Instead of taking a single radix, like parseInt('beef', 16) and (48879).toString(16), they will take one radix per each digit.
function pack(digits, radixes) {
var n = 0;
for (var i = 0; i < digits.length; i++) {
n = n * radixes[i] + digits[i];
}
return n;
}
function unpack(n, radixes) {
var digits = [];
for (var i = radixes.length - 1; i >= 0; i--) {
digits.unshift(n % radixes[i]);
n = Math.floor(n / radixes[i]);
}
return digits;
}
Does that even work?
// Decimal system
pack([4, 2], [10, 10]); // => 42
// Binary system
pack([1, 0, 1, 0, 1, 0], [2, 2, 2, 2, 2, 2]); // => 42
// Factorial system
pack([1, 3, 0, 0, 0], [5, 4, 3, 2, 1]); // => 42
And now backwards:
unpack(42, [10, 10]); // => [4, 2]
unpack(42, [5, 4, 3, 2, 1]); // => [1, 3, 0, 0, 0]
This is so beautiful. Now let's apply this parametric number system to the problem of permutations. We'll consider length 2 permutations of A, B, C, D. What's the total number of them? Let's see: first we draw one of the 4 items, then one of the remaining 3, that's 4 * 3 = 12 ways to draw 2 items. These 12 ways can be packed into integers [0..11]. So, let's pretend we've packed them already, and try unpacking:
for (var i = 0; i < 12; i++) {
console.log(unpack(i, [4, 3]));
}
// [0, 0], [0, 1], [0, 2],
// [1, 0], [1, 1], [1, 2],
// [2, 0], [2, 1], [2, 2],
// [3, 0], [3, 1], [3, 2]
These numbers represent choices, not indexes in the original array. [0, 0] doesn't mean taking A, A, it means taking item #0 from A, B, C, D (that's A) and then item #0 from the remaining list B, C, D (that's B). And the resulting permutation is A, B.
Another example: [3, 2] means taking item #3 from A, B, C, D (that's D) and then item #2 from the remaining list A, B, C (that's C). And the resulting permutation is D, C.
This mapping is called Lehmer code. Let's map all these Lehmer codes to permutations:
AB, AC, AD, BA, BC, BD, CA, CB, CD, DA, DB, DC
That's exactly what we need. But if you look at the unpack function you'll notice that it produces digits from right to left (to reverse the actions of pack). The choice from 3 gets unpacked before the choice from 4. That's unfortunate, because we want to choose from 4 elements before choosing from 3. Without being able to do so we have to compute the Lehmer code first, accumulate it into a temporary array, and then apply it to the array of items to compute the actual permutation.
But if we don't care about the lexicographic order, we can pretend that we want to choose from 3 elements before choosing from 4. Then the choice from 4 will come out from unpack first. In other words, we'll use unpack(n, [3, 4]) instead of unpack(n, [4, 3]). This trick allows to compute the next digit of Lehmer code and immediately apply it to the list. And that's exactly how nth_permutation() works.
One last thing I want to mention is that unpack(i, [4, 3]) is closely related to the factorial number system. Look at that first tree again, if we want permutations of length 2 without duplicates, we can just skip every second permutation index. That'll give us 12 permutations of length 4, which can be trimmed to length 2.
for (var i = 0; i < 12; i++) {
var lehmer = unpack(i * 2, [4, 3, 2, 1]); // Factorial number system
console.log(lehmer.slice(0, 2));
}
It depends on the way you "sort" your permutations (lexicographic order for example).
One way to do it is the factorial number system, it gives you a bijection between [0 , n!] and all the permutations.
Then for any number i in [0,n!] you can compute the ith permutation without computing the others.
This factorial writing is based on the fact that any number between [ 0 and n!] can be written as :
SUM( ai.(i!) for i in range [0,n-1]) where ai <i
(it's pretty similar to base decomposition)
for more information on this decomposition, have a look at this thread : https://math.stackexchange.com/questions/53262/factorial-decomposition-of-integers
hope it helps
As stated on this wikipedia article this approach is equivalent to computing the lehmer code :
An obvious way to generate permutations of n is to generate values for
the Lehmer code (possibly using the factorial number system
representation of integers up to n!), and convert those into the
corresponding permutations. However the latter step, while
straightforward, is hard to implement efficiently, because it requires
n operations each of selection from a sequence and deletion from it,
at an arbitrary position; of the obvious representations of the
sequence as an array or a linked list, both require (for different
reasons) about n2/4 operations to perform the conversion. With n
likely to be rather small (especially if generation of all
permutations is needed) that is not too much of a problem, but it
turns out that both for random and for systematic generation there are
simple alternatives that do considerably better. For this reason it
does not seem useful, although certainly possible, to employ a special
data structure that would allow performing the conversion from Lehmer
code to permutation in O(n log n) time.
So the best you can do for a set of n element is O(n ln(n)) with an adapted data structure.
Here's an algorithm to convert between permutations and ranks in linear time. However, the ranking it uses is not lexicographic. It's weird, but consistent. I'm going to give two functions, one that converts from a rank to a permutation, and one that does the inverse.
First, to unrank (go from rank to permutation)
Initialize:
n = length(permutation)
r = desired rank
p = identity permutation of n elements [0, 1, ..., n]
unrank(n, r, p)
if n > 0 then
swap(p[n-1], p[r mod n])
unrank(n-1, floor(r/n), p)
fi
end
Next, to rank:
Initialize:
p = input permutation
q = inverse input permutation (in linear time, q[p[i]] = i for 0 <= i < n)
n = length(p)
rank(n, p, q)
if n=1 then return 0 fi
s = p[n-1]
swap(p[n-1], p[q[n-1]])
swap(q[s], q[n-1])
return s + n * rank(n-1, p, q)
end
The running time of both of these is O(n).
There's a nice, readable paper explaining why this works: Ranking & Unranking Permutations in Linear Time, by Myrvold & Ruskey, Information Processing Letters Volume 79, Issue 6, 30 September 2001, Pages 281–284.
http://webhome.cs.uvic.ca/~ruskey/Publications/RankPerm/MyrvoldRuskey.pdf
Here is a short and very fast (linear in the number of elements) solution in python, working for any list of elements (the 13 first letters in the example below) :
from math import factorial
def nthPerm(n,elems):#with n from 0
if(len(elems) == 1):
return elems[0]
sizeGroup = factorial(len(elems)-1)
q,r = divmod(n,sizeGroup)
v = elems[q]
elems.remove(v)
return v + ", " + ithPerm(r,elems)
Examples :
letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m']
ithPerm(0,letters[:]) #--> a, b, c, d, e, f, g, h, i, j, k, l, m
ithPerm(4,letters[:]) #--> a, b, c, d, e, f, g, h, i, j, m, k, l
ithPerm(3587542868,letters[:]) #--> h, f, l, i, c, k, a, e, g, m, d, b, j
Note: I give letters[:] (a copy of letters) and not letters because the function modifies its parameter elems (removes chosen element)
The following code computes the kth permutation for given n.
i.e n=3.
The various permutations are
123
132
213
231
312
321
If k=5, return 312.
In other words, it gives the kth lexicographical permutation.
public static String getPermutation(int n, int k) {
char temp[] = IntStream.range(1, n + 1).mapToObj(i -> "" + i).collect(Collectors.joining()).toCharArray();
return getPermutationUTIL(temp, k, 0);
}
private static String getPermutationUTIL(char temp[], int k, int start) {
if (k == 1)
return new String(temp);
int p = factorial(temp.length - start - 1);
int q = (int) Math.floor(k / p);
if (k % p == 0)
q = q - 1;
if (p <= k) {
char a = temp[start + q];
for (int j = start + q; j > start; j--)
temp[j] = temp[j - 1];
temp[start] = a;
}
return k - p >= 0 ? getPermutationUTIL(temp, k - (q * p), start + 1) : getPermutationUTIL(temp, k, start + 1);
}
private static void swap(char[] arr, int j, int i) {
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
private static int factorial(int n) {
return n == 0 ? 1 : (n * factorial(n - 1));
}
It is calculable. This is a C# code that does it for you.
using System;
using System.Collections.Generic;
namespace WpfPermutations
{
public class PermutationOuelletLexico3<T>
{
// ************************************************************************
private T[] _sortedValues;
private bool[] _valueUsed;
public readonly long MaxIndex; // long to support 20! or less
// ************************************************************************
public PermutationOuelletLexico3(T[] sortedValues)
{
if (sortedValues.Length <= 0)
{
throw new ArgumentException("sortedValues.Lenght should be greater than 0");
}
_sortedValues = sortedValues;
Result = new T[_sortedValues.Length];
_valueUsed = new bool[_sortedValues.Length];
MaxIndex = Factorial.GetFactorial(_sortedValues.Length);
}
// ************************************************************************
public T[] Result { get; private set; }
// ************************************************************************
/// <summary>
/// Return the permutation relative to the index received, according to
/// _sortedValues.
/// Sort Index is 0 based and should be less than MaxIndex. Otherwise you get an exception.
/// </summary>
/// <param name="sortIndex"></param>
/// <param name="result">Value is not used as inpu, only as output. Re-use buffer in order to save memory</param>
/// <returns></returns>
public void GetValuesForIndex(long sortIndex)
{
int size = _sortedValues.Length;
if (sortIndex < 0)
{
throw new ArgumentException("sortIndex should be greater or equal to 0.");
}
if (sortIndex >= MaxIndex)
{
throw new ArgumentException("sortIndex should be less than factorial(the lenght of items)");
}
for (int n = 0; n < _valueUsed.Length; n++)
{
_valueUsed[n] = false;
}
long factorielLower = MaxIndex;
for (int index = 0; index < size; index++)
{
long factorielBigger = factorielLower;
factorielLower = Factorial.GetFactorial(size - index - 1); // factorielBigger / inverseIndex;
int resultItemIndex = (int)(sortIndex % factorielBigger / factorielLower);
int correctedResultItemIndex = 0;
for(;;)
{
if (! _valueUsed[correctedResultItemIndex])
{
resultItemIndex--;
if (resultItemIndex < 0)
{
break;
}
}
correctedResultItemIndex++;
}
Result[index] = _sortedValues[correctedResultItemIndex];
_valueUsed[correctedResultItemIndex] = true;
}
}
// ************************************************************************
/// <summary>
/// Calc the index, relative to _sortedValues, of the permutation received
/// as argument. Returned index is 0 based.
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public long GetIndexOfValues(T[] values)
{
int size = _sortedValues.Length;
long valuesIndex = 0;
List<T> valuesLeft = new List<T>(_sortedValues);
for (int index = 0; index < size; index++)
{
long indexFactorial = Factorial.GetFactorial(size - 1 - index);
T value = values[index];
int indexCorrected = valuesLeft.IndexOf(value);
valuesIndex = valuesIndex + (indexCorrected * indexFactorial);
valuesLeft.Remove(value);
}
return valuesIndex;
}
// ************************************************************************
}
}
If you store all the permutations in memory, for example in an array, you should be able to bring them back out one at a time in O(1) time.
This does mean you have to store all the permutations, so if computing all permutations takes a prohibitively long time, or storing them takes a prohibitively large space then this may not be a solution.
My suggestion would be to try it anyway, and come back if it is too big/slow - there's no point looking for a "clever" solution if a naive one will do the job.

Categories