efficient way to find unequal partitions of an integer - php

I have total partitions of an integer and I want only those partitions which have all the values unequal. For ex.-Partitions of 3 are {1,1,1,1},{2,2},{3,1},{1,1,2} and {4}. So, the required unequal partitions are {3,1} and {4} because they contain no equal elements.
The code that I have used for finding all partitions is provided below. I can filter the partitions to get the desired result, but I want some efficient way to find all the partitions, which have no equal terms in them, without finding all the partitions. I have searched the net and stackoverflow but nothing states exactly the problem that I am facing. Every idea is appreciated. Thanks.
function total_partitions_of_a_number($n) {# base case of recursion: zero is the sum of the empty list
if(!$n) return array(array()); # return empty array
# modify partitions of n-1 to form partitions of n
foreach(total_partitions_of_a_number($n-1) as $p) { # recursive call
$a[] = array_merge(array(1), $p); # "yield" array [1, p...]
if($p && (count($p) < 2 || $p[1] > $p[0])) { # p not empty, and length < 2 or p[1] > p[0]
++$p[0]; # increment first item of p
$a[] = $p; # "yield" p
}
}
return $a; # return all "yielded" values at once
}

So you want only partitions where any given component appears no more than once? The recursion is simple.
Reduce it to the problem of solving for the partitions of N, such that no element in the set is larger than some value a (a will initially be N.) Now, a either does or does not appear in the partition. Depending on this, then you will both recursively solve for the partitions of (N-a), such that no element is larger than a-1, and for the partitions of N such that no member is larger than a-1.
In either case, the recursion is well posed, and will terminate when it is no longer possible to solve the problem, thus, when a*(a+1)/2 < N. Of course, when a*(a+1)/2 = N, you can also quickly terminate the recursion as the solution is then unique.

Related

Getting every combination of X numbers given Y numbers?

I've come to a mathematical problem which for I can't program the logic.
Let me explain it with an example:
Let's say I have 4 holes and 3 marbles, the holes are in order and my marbles are A,B and C and also in order.
I need to get every posible ORDERED combination:
ABC4
AB3C
A2BC
1ABC
This is very simple, but what if the number of holes changes? Let's say now I have 5 holes.
ABC45
AB3C5
A2BC5
1ABC5
AB34C
A2B4C
1AB4C
A23BC
1A3BC
12ABC
Now let's say we have 5 holes and 4 marbles.
ABCD5
ABC4D
AB3CD
A2BCD
1ABCD
And this can be any number of holes and any number of marbles.
The number of combinations is given by:
$combinations = factorial($number_of_holes)/(factorial($number_of_marbles)*factorial($number_of_holes-$number_of_marbles)))
(Here it is the factorial function in case you need it)
function factorial($number) {
if ($number < 2) {
return 1;
} else {
return ($number * factorial($number-1));
}
}
What I need and can't figure out how to program, is a function or a loop or something, that returns an array with the position of the holes, given X numbers of holes and Y number of marbles.
For first example it would be: [[4],[3],[2],[1]], for second: [[4,5],[2,5],[1,5],[3,4],[2,4],[1,5],[2,3],[1,3],[1,2]], for third: [[5],[4],[3],[2],[1]].
It doesn't have to be returned in order, I just need all the elements.
As you can see, another approach is the complementary or inverse or don't know how to call it, but the solution is every combinations of X number of free holes given Y number of holes, so, If I have 10 holes, and 5 marbles, there would be 5 free holes, the array returned would be every combination of 5 that can be formed with (1,2,3,4,5,6,7,8,9,10), which are 252 combinations, and what I need is the 252 combinations.
Examples for the 2nd approach:
Given an array=[1,2,3,4], return every combination for sets of 2 and 3.
Sets of 2
[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
Sets of 3
[[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
What I need is the logic to do this, I'm trying to do it in PHP, but I just can't figure out how to do it.
The function would receive the array and the set size and would return the array of sets:
function getCombinations($array,$setize){
//magic code which I can't figure out
return array(sets);
}
I hope this is clear enough and someone can help me, I've been stuck for several days now, but it seems to be just too much for me to handle by myself.
This post, PHP algorithm to generate all combinations of a specific size from a single set, is for all possible combinations, repeating the elements and order doesn't matter, its a good lead, I did read it, but it doesn't solve my problem, it's very different. I need them without repeating the elements and ordered as explained.
Let's say if I have already a set of [3,4] in my array, I don't want [4,3] as an other set.
Here's a recursive solution in PHP:
function getCombinations($array, $setsize){
if($setsize == 0)
return [[]];
// generate combinations including the first element by generating combinations for
// the remainder of the array with one less element and prepending the first element:
$sets = getCombinations(array_slice($array, 1), $setsize - 1);
foreach ($sets as &$combo) {
array_unshift($combo, $array[0]);
}
// generate combinations not including the first element and add them to the list:
if(count($array) > $setsize)
$sets = array_merge($sets, getCombinations(array_slice($array, 1), $setsize));
return $sets;
}
// test:
print_r(getCombinations([1, 2, 3, 4], 3));
Algorithm works like this:
If setsize is 0 then you return a single, empty combination
Otherwise, generate all combinations that include the first element, by recursively generating all combinations off the array excluding the first element with setsize - 1 elements, and then prepending the first element to each of them.
Then, if the array size is greater than setsize (meaning including the first element is not compulsory), generate all the combinations for the rest of the list and add them to the ones we generated in the second step.
So basically at each step you need to consider whether an element will be included or excluded in the combination, and merge together the set of combinations representing both choices.

Two-way hashing of fixed range numbers

I need to create a function which takes a single integer as argument in the range 0-N and returns a seemingly random number in the same range.
Each input number should always have exactly one output and it should always be the same.
Such a function would produce something like this:
f(1) = 4
f(2) = 1
f(3) = 5
f(4) = 2
f(5) = 3
I believe this could be accomplished by some kind of a hashing algorithm? I don't need anything complex, just not something too simple like f(1) = 2, f(2) = 3 etc.
The biggest issue is that I need this to be reversible. E.g. the above table should be true left-to-right as well as right-to-left, using a different function for the right-to-left conversion is fine.
I know the easiest way is to create an array, shuffle it and just store the relations in a db or something, but as I need N to be quite large I'd like to avoid this if possible.
Edit: For my particular case N is a specific number, it's exactly 16777216 (64^4).
If the range is always a power of two -- like [0,16777216) -- then you can use exclusive-or just as #MarkBaker suggested. It just doesn't work so easily if your range is not a power of two.
You can use addition and subtraction modulo N, although these alone are too obvious, so you have to combine it with something else.
You can also do multiplication modulo-N, but reversing that is complicated. To make it simpler, we can isolate the bottom eight bits and multiply those and add them in a way that doesn't interfere with those bits so we can use them again to reverse the operation.
I don't know PHP so I'm going to give an example in C, instead. Maybe it's the same.
int enc(int x) {
x = x + 4799 * 256 * (x % 256);
x = x + 8896843;
x = x ^ 4777277;
return (x + 1073741824) % 16777216;
}
And to decode, play the operations back in reverse order:
int dec(int x) {
x = x + 1073741824;
x = x ^ 4777277;
x = x - 8896843;
x = x - 4799 * 256 * (x % 256);
return x % 16777216;
}
That 1073741824 must be a multiple of N, and 256 must be a factor of N, and if N is not a power of two then you can't (necessarily) use exclusive-or (^ is exclusive-or in C and I assume in PHP too). The other numbers you can fiddle with, and add and remove stages, at your leisure.
The addition of 1073741824 in both functions is to ensure that x stays positive; this is so that the modulo operation doesn't ever give a negative result, even after we've subtracted values from x which might have made it go negative in the interim.
I offered to describe how I "randomly" scramble up 9-digit SSNs when producing research data sets. This does not replace or hash an SSN. It re-orders the digits. It is difficult to put the digits back in the correct order if you don't know the order in which they were scrambled. I have a gut feeling that this is not what the questioner really wants. So, I am happy to delete this answer if it is deemed off-topic.
I know that I have 9 digits. So, I start with an array that has 9 index values in order:
$a = array(0,1,2,3,4,5,6,7,8);
Now, I need to turn a key that I can remember into a way to shuffle the array. The shuffling has to be the same order for the same key every time. I use a couple tricks. I use crc32 to turn a word into a number. I use srand/rand to get a predictable order of random values. Note: mt_rand no longer produces the same sequence of random digits with the same seed, so I have to use rand.
srand(crc32("My secret key"));
usort($a, function($a, $b) { return rand(-1,1); });
The array $a still has the digits 0 through 8, but they are shuffled. If I use the same keyword I will get the same shuffled order every time. That lets me repeat this every month and get the same result. Then, with a shuffled array, I can pick the digits off the SSN. First, I ensure it has 9 characters (some SSNs are sent as integers and a leading 0 is omitted). Then, I build a masked SSN by picking the digits using $a.
$ssn = str_pad($ssn, 9, '0', STR_PAD_LEFT);
$masked_ssn = '';
foreach($a as $i) $masked_ssn.= $ssn{$i};
$masked_ssn will now have all the digits in $ssn, but in a different order. Technically, there are keywords that make $a become the original ordered array after shuffling, but that is very very rare.
Hopefully this makes sense. If so, you can do it all much faster. If you turn the original string into an array of characters, you can shuffle the array of characters. You just need to reseed rand every time.
$ssn = "111223333"; // Assume I'm using a proper 9-digit SSN
$a = str_split($ssn);
srand(crc32("My secret key"));
usort($a, function($a, $b) { return rand(-1,1); });
$masked_ssn = implode('', $a);
This is not really faster in a runtime way because rand is a rather expensive function and you run rand a hell of lot more here. If you are masking thousands of values as I do, you will want to use an index array that is shuffled just once, not a shuffling for every value.
Now, how do I undo it? Assume I'm using the first method with the index array. It will be something like $a = {5, 3, 6, 1, 0, 2, 7, 8, 4}. Those are the indexes for the original SSN in the masked order. So, I can easily build the original SSN.
$ssn = '000000000'; // I like to define all 9 characters before I start
foreach($a as $i=>$j) $ssn[$j] = $masked_ssn{$i};
As you can see, $i counts from 0 to 8 across the masked SSN. $j counts 5, 3, 6... and puts each value from the masked SSN in the correct place in the original SSN.
Looks like you've got good answer, but still there is an alternative. Linear Congruential Generator (LCG) could provide 1-to-1 mapping and it is known to be a reversible using Euclid's algorithm. For 24bit
Xi = [(A * Xi-1) + C] Mod M
where M = 2^24 = 16,777,216
A = 16,598,013
C = 12,820,163
For LCG reversability take a look at Reversible pseudo-random sequence generator

Big-O notation how to work through it php or javascript

Ok I am just learning about Big-O and someone gave me a conceptual question, to take as a means of trying to learn. However just barely starting out with Big-O I only know concept per say.
I've been told If I take an array of sorted INT values how would I write a function that essentially would return true (or false) if the sum of any two of the numbers in the array equal zero. So my assumption is I would have an array like.
array("0","1","2","3","4")
Just an example Im sure the array is much larger. What I am trying to figure out how is how can I really do that? I don't want to iterate over the array x times where x is the count of the array and then some to try every combination, thats just insane and if the array is large enough all Ill do is run out of memory and either bottle neck myself server side or client side depending on the route I run with it javascript or php.
So whats a good way of tackling that cause I sure have no decent clue at the moment.
Big O notation is about classifying how "intense" certain algorithms are.
To take your example of a poor algorithm to solve that problem, you have n elements of an array, and one way to see if any combination of elements sums to zero is for each 1 element, check all of the other n-1 elements for the zero sum.
Big O ignores the constants, so "for each element" = n you check n elements, and get O(n*n) or O(n^2).
Like you said, there's probably a better way. Since the array is sorted, try to think of some properties of that array, like big numbers at one end and small at the other.
That should help you think of an algorithm that has a lower complexity than O(n^2).
Since it's sorted, you can find the value closest to zero in O(log(n)) time through binary search. That value splits the array in two subarrays, one with values less than zero (call it A) and one with values greater than zero (call it B). Obvisouly, two values a and b sum up to zero if and only if a = -b. So you can do:
var i = 0;
var j = 0;
while (i < A.length && j < B.length) {
if (A[i] == -B[j]) {
return true;
} else if (A[i] < -B[j]) {
i++;
} else {
j++;
}
}
return false;
The complexity of the algorithm above is O(n/2)=O(n), and since O(n) > O(log(n)) (binary search), then the overall complexity is still O(n). Note then, that you could have iterated over the array instead of using binary search and the complexity would have been the same.
That actually makes me think you can do it all in a single loop. Just keep two pointers, one at the start of the array, one at the end, and move the progressively towards the middle. Complexity is is still O(n/2) = O(n):
var i = myArray.length-1;
var j = 0;
while (i > j) {
if (myArray[i] == -myArray[j]) {
return true;
} else if (myArray[i] > -myArray[j]) {
i--;
} else {
j++;
}
}
return false;

Multiple foreach with over 37 million possibilities

I've been tasked with creating a list of all possibilities using data in 8 blocks.
The 8 blocks have the following number of possibilities:
*Block 1: 12 possibilities
*Block 2: 8 possibilities
*Block 3: 8 possibilities
*Block 4: 11 possibilities
*Block 5: 16 possibilities
*Block 6: 11 possibilities
*Block 7: 5 possibilities
*Block 8: 5 possibilities
This gives a potential number of 37,171,200 possibilities.
I tried simply doing and limiting only to displaying the values returned with the correct string length like so:
foreach($block1 AS $b1){
foreach($block2 AS $b2){
foreach($block3 AS $b3){
foreach($block4 AS $b4){
foreach($block5 AS $b5){
foreach($block6 AS $b6){
foreach($block7 AS $b7){
foreach($block8 AS $b8){
if (strlen($b1.$b2.$b3.$b4.$b5.$b6.$b7.$b8) == 16)
{
echo $b1.$b2.$b3.$b4.$b5.$b6.$b7.$b8.'<br/>';
}
}
}
}
}
}
}
}
}
However the execution time was far too long to compute. I was wondering if anyone knew of a simpler way of doing this?
You could improve your algorithm by caching the string prefixes and remember their lengths. Then you don’t have to do that for each combination.
$len = 16:
// array for remaining characters per level
$r = array($len);
// array of level parts
$p = array();
foreach ($block1 AS &$b1) {
// skip if already too long
if (($r[0] - strlen($b1)) <= 0) continue;
$r[1] = $r[0] - strlen($b1);
foreach ($block2 AS &$b2) {
if (($r[1] - strlen($b2)) <= 0) continue;
$r[2] = $r[1] - strlen($b2);
foreach ($block3 AS $b3) {
// …
foreach ($block8 AS &$b8) {
$r[8] = $r[7] - strlen($b8);
if ($r[8] == 0) {
echo implode('', $p).'<br/>';
}
}
}
}
}
Additionally, using references in foreach will stop PHP using a copy of the array internally.
You could try to store the precomputed part the concatenated string known at each of the previous lelels for later reuse, avoiding concatenating everything in the innermost loop
foreach($block7 AS $b7){
$precomputed7 = $precomputed6.$b7
foreach($block8 AS $b8){
$precomputed8 = $precomputed7.$b8
if (strlen($precomputed8) == 16) {
echo $precomputed8.'<br/>';
}
}
}
Doing this analogously for precedent levels. Then you could try to test at one of the higher loop level for strings that are already longer as 16 chars. You can shortcut and avoid trying out other possibilities. But beware calculating the length of the string costs much performance, maybe is the latter improvement not worth it at all, depending on the input data.
Another idea is to precalculate the lengths for each block and then recurse on the array of lengths, calculating sums should be faster than concatenating and computing the length of strings. For the Vector of indexes that match the length of 16, you can easily output the full concatenated string.
Since you have that length requirement of 16 and assuming each (i) possibility in each (b) of the eight blocks has length x_i_b you can get some reduction by some cases becoming impossible.
For example, say we have length requirement 16, but only 4 blocks, with possibilities with lengths indicated
block1: [2,3,4]
block2: [5,6,7]
block3: [8,9,10]
block4: [9,10,11]
Then all of the possibilities are impossible since block 4's lengths are all too large to permit any combination of blocks 1 - 3 of making up the rest of the 16.
Now if you're length is really 16 that means that your (possible) lengths range from 1 to 9, assumng no 0 lengths.
I can see two ways of approaching this:
Greedy
Dynamic Programming
Perhaps even combine them. For the Greedy approach, pick the biggest possibility in all the blocks, then the next biggest etc, follow that through until you cross your threshold of 16. If you got all the blocks, then you can emit that one.
Whether or not you got on threshold or not, you can then iterate through the possibilities.
The dynamic appraoch means that you should store some of the results that you compute already. Like a selection from some of the blocks that gives you a length of 7, you don't need to recompute that in future, but you can iterate through the remaining blocks to see if you can find a combination to give you lenth 9.
EDIT: This is kind of like the knapsack problem but with the additional restriction of 1 choice per block per instance. Anyway, in terms of other optimizations definitely pre process the blocks into arrays of lengths only and keep a running sum at each iteration level. So you only do 1 sum per each iteration of each loop, rather than 8 sums per each iteration. Also only str concat if you need to emit the selection.
If you don't want a general solution (probably easier if you don't) then you can hand code alot of problem instance specific speedups by excluding the largest too small combination of lengths (and all selections smaller than that) and excluding the smallest too large combination of lengths (and all selections larger).
If you can express this as a nested array, try a RecursiveIteratorIterator, http://php.net/manual/en/class.recursiveiteratoriterator.php

PHP: Permutations of 26 letters in X characters

I have an alphabet array with 26 characters A..Z .
I am searching for a performant algorithm that lists all permutations that fill an array of length X without any repeating characters.
Examples:
X=3 . Target array: _ _ _
Permutations are A B C until Z Y X .
X=4 . Target array: _ _ _ _
Permutations are A B C D until Z Y X W
X=5 . Target array: _ _ _ _ _
Permutations are A B C D E until Z Y X W V
(Sorry, I don't know how this kind of algorithm is named)
Thanks in advance.
Code in C, Delphi or Java is also OK, since it can be easy translated.
A simple solution is a recursive one
char current_combination[27];
int char_used[26];
void enumerate(int i, int n)
{
for (int j=0; j<26; j++)
{
if (!char_used[j])
{
char_used[j] = 1;
current_combination[i] = 'A' + j;
if (i+1 == n)
{
puts(current_combination);
}
else
{
enumerate(i+1, n);
}
char_used[j] = 0;
}
}
}
The function above accepts the index i of the character to be computed and the total number n of characters in a combination (the code assumes i<n). It keeps the current combination and the array of flags for already used variables in globals to avoiding copying them around.
To generate for example all combinations of length 5 call enumerate(0, 5).
Note that the total number of combinations grows very fast. For example for n=6 there are 165,765,600 combinations, with more than 1Gb of output.
I'd take a simple brute force approach though do understand the number of permutations can get up there as the number is 26!/(26-x)! which can be rather large as for 3 there are 15,600 permutations and for 5 there are 7,893,600 permutations which isn't exactly small. Basically you could just loop through all the values with loops in loops that unfortunately would be O(n^x) where x is the number of characters since the nesting of loops causing the complexity jump.
Something to consider is how finely are you examining complexity here. For example, while you could consider ways to go about being clever in the first pair of loops to avoid duplication, the third loop in becomes a bit trickier though if you started with a List of 26 letters and removed the previous ones, this would make the last loop be a simply iterative as you know there isn't any duplicates though this can be expensive in terms of memory consumed in having to make copies of the list on each pass from the outer loop. Thus the first time, you'd go through AB_ and then AC_ and so forth, but the copying of the list may be where this gets expensive in terms of operations as there would be thousands of times that the list is copied that one could wonder if that is more efficient than doing comparisons.
Are you sure you want to see all permutations? If you have X=3, you will have 26*25*24 combinations = 15600. And if X=5 number of combinations is equal to 7893600.
You need to randomly select one letter(or array index) and store it somewhere and on each iteration you should check if this letter(or index) has been already selected on one of the previous iteration. After this you will get random sequence which length is X characters. You need to store it too. Then you need to reapeat all operation made on the previous step and also you have to check if there is random sequense with subsequence you have been generating now.
Or you could use direct enumeration.
Sorry for not satisfactory english. I tried to be clear.
I hope it will be usefull.

Categories