Optimal strategy for prime number tower game - algorithm

Consider the following game:
You have a tower of N cubes. Each turn, the player can take from the tower only prime number, or power of prime number of cubes.
The winner it the last player who plays, meaning the last player to take prime number (or power of prime number) of cubes and there are no more cubes left.
Notes:
1) The run time for each turn need to be minimal.
2) There is no limit for numbers of turns
The goal:
a) Find an algorithm to win the game and also to determine if we need to be the first player or the second player, when there is only one tower.
b) same as a, but now we have 2 towers with different number of cubes.
Example:
if we have the number N=6, if we play first:
we can take 1, but player-2 will take 5 and win
we can take 2, but player-2 will take 4 and win (2 is prime number and power of 2)
we can take 3, but player-2 will take 3 and win
we can take 4, but player-2 will take 2 and win
we can take 5, but player-2 will take 1 and win
Therefor in this case the algorithm should determine that we must play second, and in this specific case we can choose to drop any amount of cubes we want.

The multiple pile version is a finite additive game, in that each tower is a separate game, people can choose which one to play in next, and the games are guaranteed to terminate in a finite number of steps with a clear victor. All additive games can be reduced to Nim.
Specifically the nim score of losing immediately is 0, winning immediately is 1, and otherwise a single tower of size n has the smallest possible nim score that you can't reach in one move, where possible nim scores are drawn from 0, 1, 2, ....
This allows us to recursively calculate nim scores for a single tower. The winning strategy will be to try to always give the other person a score of 0, and eventually you will make them lose. Note that if you're given a position with a nim score that is greater than 0, you can always find a move that gives the other person a score of 0 (if there was no way to get 0, then your nim score would have been 0). So if you've been given a position with a score of 0 and the other person plays correctly, you will always get a score of 0 and eventually we lose.
Now here is the basic result about additive games. If you can calculate nim scores for each of multiple towers, the nim score of the combination is just the xor of the individual nim scores.
So here are the first few nim scores.
0: 0 (you just lost)
1: 1 (nim(1-1) = 0)
2: 2 (nim(2-2) = 0, nim(2-1) = 1)
3: 3 (nim(3-3) = 0, nim(3-2) = 1, nim(3-1) = 2)
4: 4 (nim(4-4) = 0, nim(4-3) = 1, nim(4-2) = 2, nim(4-1) = 3)
5: 5 (nim(5-5) = 0, nim(5-4) = 1, nim(5-3) = 2, nim(5-2) = 3, nim(5-1) = 4)
6: 0 (can't get 0)
7: 1 (nim(7-7) = 0)
8: 2 (nim(8-8) = 0, nim(8-7) = 1)
9: 3 (nim(9-9) = 0, nim(9-8) = 1, nim(9-7) = 2)
10: 4 (nim(10 - 4) = 0, nim(10-9) = 2, nim(10-8) = 2, nim(10-7) = 3)
And so on. It is easy to calculate this recursively. Memoize, and it will be O(n**2 / log(n)) (For each of n numbers, construct the set of nim values that you could reach after all O(n / log(n)) possible moves, then start counting from 0 up until after no more than O(n / log(n)) possible values you've found the first that is not achievable.)
To actually play it, you should store not only the nim score of the tower, but a lookup for how to get all of the better nim values that can be achieved from it. In a single tower version, that lets you immediately know how to play. In the multi-tower version it is slightly more complicated. When you're handed a position that has a non-zero nim score you should look for a tower whose nim score has a 1 in the leading binary digit of the score. You want to move with that tower to make its new nim score be the xor of the other towers. This new score will always be smaller than its current nim score, and therefore you will be able to make the move and hand back a 0 score.

I found an alternative solution-
You need always keep the number in the tower to be modulo 6 equal 0 so the rival can't take prime number (or power of prime number) of cubes.
For 2 towers, you need that both of the towers will be equally modulo, means that they both modulo 6 need to be the same.

Related

Algorithm for calculating number of ways to put a line of balls in boxes

I'm thinking of an algorithm for calculating the number of ways to partition balls, here is the setup of the problem:
The balls are in a line with numbers, i.e. 1, 2, 3, 4, 5,...N, we cannot change the order of the balls.
We want to group the balls into boxes, and we can use any number of boxes but no box can be empty.
Two groups are considered the same only if they are identical in number of boxes and the balls for corresponding boxes.
One example of grouping the balls is |1, 2|3, 4, 5|6| and it is (obviously) different from |1, 2, 3|4, 5|6|
I would like to calculate the number of ways of grouping the balls. Normally I would try a brutal force search but I don't even know where to start.
And here is some interesting extensions of the problem:
What is we have the requirement that the number of balls in a latter box needs to be equal to or larger than the previous box? For example, we can do |1|2, 3|4, 5|6, 7, 8| but we cannot do |1, 2, 3|4, 5|6, 7, 8|
What if the number of balls in a latter box needs to be at least k times of its previous box, where k is a given value and is at least 1?
This is the problem of integer compositions.
Formula is rather simple - there is 2^(n-1) compositions.
The first extension is the problem of integer partitions.
There is no closed-formula known.
Example of implementation of Euler's pentagonal number theorem
For the second extension it is possible to make recursive function like this (not checked, just idea)
def partsktimes(x, current, k)
if x < 0:
return 0
if x == 0:
return 1
result = 0
for i in range(current*k, x+1):
result += partsktimes(x - i, i, k)

Ugly Number - Mathematical intuition for dp

I am trying find the "ugly" numbers, which is a series of numbers whose only prime factors are [2,3,5].
I found dynamic programming solution and wanted to understand how it works and what is the mathematical intuition behind the logic.
The algorithm is to keep three different counter variable for a multiple of 2, 3 and 5. Let's assume i2,i3, and i5.
Declare ugly array and initialize 0 index to 1 as the first ugly number is 1.
Initialize i2=i3=i4=0;
ugly[i] = min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5) and increment i2 or i3 or i5 which ever index was chosen.
Dry run:
ugly = |1|
i2=0;
i3=0;
i5=0;
ugly[1] = min(ugly[0]*2, ugly[0]*3, ugly[0]*5) = 2
---------------------------------------------------
ugly = |1|2|
i2=1;
i3=0;
i5=0;
ugly[2] = min(ugly[1]*2, ugly[0]*3, ugly[0]*5) = 3
---------------------------------------------------
ugly = |1|2|3|
i2=1;
i3=1;
i5=0;
ugly[3] = min(ugly[1]*2, ugly[1]*3, ugly[0]*5) = 4
---------------------------------------------------
ugly = |1|2|3|4|
i2=2;
i3=1;
i5=0;
ugly[4] = min(ugly[2]*2, ugly[1]*3, ugly[0]*5) = 5
---------------------------------------------------
ugly = |1|2|3|4|5|
i2=2;
i3=1;
i5=1;
ugly[4] = min(ugly[2]*2, ugly[1]*3, ugly[0]*5) = 6
---------------------------------------------------
ugly = |1|2|3|4|5|6|
I am getting lost how six is getting formed from 2's index. Can someone explain in an easy way?
Every "ugly" number (except 1) can be formed by multiplying a smaller ugly number by 2, 3, or 5.
So let's say that the ugly numbers found so far are [1,2,3,4,5]. Based on that list we can generate three sequences of ugly numbers:
Multiplying by 2, the possible ugly numbers are [2,4,6,8,10]
Multiplying by 3, the possible ugly numbers are [3,6,9,12,15]
Multiplying by 5, the possible ugly numbers are [5,10,15,20,25]
But we already have 2,3,4, and 5 in the list, so we don't care about values less than or equal to 5. Let's mark those entries with a - to indicate that we don't care about them
Multiplying by 2, the possible ugly numbers are [-,-,6,8,10]
Multiplying by 3, the possible ugly numbers are [-,6,9,12,15]
Multiplying by 5, the possible ugly numbers are [-,10,15,20,25]
And in fact, all we really care about is the smallest number in each sequence
Multiplying by 2, the smallest number greater than 5 is 6
Multiplying by 3, the smallest number greater than 5 is 6
Multiplying by 5, the smallest number greater than 5 is 10
After adding 6 to the list of ugly numbers, each sequence has one additional element:
Multiplying by 2, the possible ugly numbers are [-,-,-,8,10,12]
Multiplying by 3, the possible ugly numbers are [-,-,9,12,15,18]
Multiplying by 5, the possible ugly numbers are [-,10,15,20,25,30]
But the elements from each sequence that are useful are:
Multiplying by 2, the smallest number greater than 6 is 8
Multiplying by 3, the smallest number greater than 6 is 9
Multiplying by 5, the smallest number greater than 6 is 10
So you can see that what the algorithm is doing is creating three sequences of ugly numbers. Each sequence is formed by multiplying all of the existing ugly numbers by one of the three factors.
But all we care about is the smallest number in each sequence (larger than the largest ugly number found so far).
So the indexes i2, i3, and i5 are the indexes into the corresponding sequences. When you use a number from a sequence, you update the index to point to the next number in that sequence.
The intuition is the following:
any ugly number can be written as the product between 2, 3 or 5 and another (smaller) ugly number.
With that in mind, the solution that is mentioned in the question keeps track of i2, i3 and i5, the indices of the smallest ugly numbers generated so far, which multiplied by 2, 3, respectively 5 lead to a number that was not already generated. The smallest of these products is the smallest ugly number that was not already generated.
To state this differently, I believe that the following statement from the question might be the source of some confusion:
The algorithm is to keep three different counter variable for a
multiple of 2, 3 and 5. Let's assume i2,i3, and i5.
Note, for example, that ugly[i2] is not necessarily a multiple of 2. It is simply the smallest ugly number for which 2 * ugly[i2] is greater than ugly[i] (the largest ugly number known so far).
Regarding how the number 6 is generated in the next step, the procedure is shown below:
ugly = |1|2|3|4|5
i2 = 2;
i3 = 1;
i5 = 1;
ugly[5] = min(ugly[2]*2, ugly[1]*3, ugly[1]*5) = min(3*2, 2*3, 2*5) = 6
---------------------------------------------------
ugly = |1|2|3|4|5|6
i2 = 3
i3 = 2
i5 = 1
Note that here both i2 and i3 need to be incremented after generating the number 6, because both i2*2, as well as i3*3 produced the same next smallest ugly number.

Find a period of eventually periodic sequence

Short explanation.
I have a sequence of numbers [0, 1, 4, 0, 0, 1, 1, 2, 3, 7, 0, 0, 1, 1, 2, 3, 7, 0, 0, 1, 1, 2, 3, 7, 0, 0, 1, 1, 2, 3, 7]. As you see, from the 3-rd value the sequence is periodic with a period [0, 0, 1, 1, 2, 3, 7].
I am trying to automatically extract this period from this sequence. The problem is that neither I know the length of the period, nor do I know from which position the sequence becomes periodic.
Full explanation (might require some math)
I am learning combinatorial game theory and a cornerstone of this theory requires one to calculate Grundy values of a game graph. This produces infinite sequence, which in many cases becomes eventually periodic.
I found a way to efficiently calculate grundy values (it returns me a sequence). I would like to automatically extract offset and period of this sequence. I am aware that seeing a part of the sequence [1, 2, 3, 1, 2, 3] you can't be sure that [1, 2, 3] is a period (who knows may be the next number is 4, which breaks the assumption), but I am not interested in such intricacies (I assume that the sequence is enough to find the real period). Also the problem is the sequence can stop in the middle of the period: [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, ...] (the period is still 1, 2, 3).
I also need to find the smallest offset and period. For example for original sequence, the offset can be [0, 1, 4, 0, 0] and the period [1, 1, 2, 3, 7, 0, 0], but the smallest is [0, 1, 4] and [0, 0, 1, 1, 2, 3, 7].
My inefficient approach is to try every possible offset and every possible period. Construct the sequence using this data and check whether it is the same as original. I have not done any normal analysis, but it looks like it is at least quadratic in terms of time complexity.
Here is my quick python code (have not tested it properly):
def getPeriod(arr):
min_offset, min_period, n = len(arr), len(arr), len(arr)
best_offset, best_period = [], []
for offset in xrange(n):
start = arr[:offset]
for period_len in xrange(1, (n - offset) / 2):
period = arr[offset: offset+period_len]
attempt = (start + period * (n / period_len + 1))[:n]
if attempt == arr:
if period_len < min_period:
best_offset, best_period = start[::], period[::]
min_offset, min_period = len(start), period_len
elif period_len == min_period and len(start) < min_offset:
best_offset, best_period = start[::], period[::]
min_offset, min_period = len(start), period_len
return best_offset, best_period
Which returns me what I want for my original sequence:
offset [0, 1, 4]
period [0, 0, 1, 1, 2, 3, 7]
Is there anything more efficient?
Remark: If there is a period P1 with length L, then there is also a period P2, with the same length, L, such that the input sequence ends exactly with P2 (i.e. we do not have a partial period involved at the end).
Indeed, a different period of the same length can always be obtained by changing the offset. The new period will be a rotation of the initial period.
For example the following sequence has a period of length 4 and offset 3:
0 0 0 (1 2 3 4) (1 2 3 4) (1 2 3 4) (1 2 3 4) (1 2 3 4) (1 2
but it also has a period with the same length 4 and offset 5, without a partial period at the end:
0 0 0 1 2 (3 4 1 2) (3 4 1 2) (3 4 1 2) (3 4 1 2) (3 4 1 2)
The implication is that we can find the minimum length of a period by processing the sequence in reverse order, and searching the minimum period using zero offset from the end. One possible approach is to simply use your current algorithm on the reversed list, without the need of the loop over offsets.
Now that we know the length of the desired period, we can also find its minimum offset. One possible approach is to try all various offsets (with the advantage of not needing the loop over lengths, since the length is known), however, further optimizations are possible if necessary, e.g. by advancing as much as possible when processing the list from the end, allowing the final repetition of the period (i.e. the one closest to the start of the un-reversed sequence) to be partial.
I would start with constructing histogram of the values in the sequence
So you just make a list of all numbers used in sequence (or significant part of it) and count their occurrence. This is O(n) where n is sequence size.
sort the histogram ascending
This is O(m.log(m)) where m is number of distinct values. You can also ignore low probable numbers (count<treshold) which are most likely in the offset or just irregularities further lowering m. For periodic sequences m <<< n so you can use it as a first marker if the sequence is periodic or not.
find out the period
In the histogram the counts should be around multiples of the n/period. So approximate/find GCD of the histogram counts. The problem is that you need to take into account there are irregularities present in the counts and also in the n (offset part) so you need to compute GCD approximately. for example:
sequence = { 1,1,2,3,3,1,2,3,3,1,2,3,3 }
has ordered histogram:
item,count
2 3
1 4
3 6
the GCD(6,4)=2 and GCD(6,3)=3 you should check at least +/-1 around the GCD results so the possible periods are around:
T = ~n/2 = 13/2 = 6
T = ~n/3 = 13/3 = 4
So check T={3,4,5,6,7} just to be sure. Use always GCD between the highest counts vs. lowest counts. If the sequence has many distinct numbers you can also do a histogram of counts checking only the most common values.
To check period validity just take any item near end or middle of the sequence (just use probable periodic area). Then look for it in close area near probable period before (or after) its occurrence. If found few times you got the right period (or its multiple)
Get the exact period
Just check the found period fractions (T/2, T/3, ...) or do a histogram on the found period and the smallest count tells you how many real periods you got encapsulated so divide by it.
find offset
When you know the period this is easy. Just scan from start take first item and see if after period is there again. If not remember position. Stop at the end or in the middle of sequence ... or on some treshold consequent successes. This is up to O(n) And the last remembered position is the last item in the offset.
[edit1] Was curious so I try to code it in C++
I simplified/skip few things (assuming at least half of the array is periodic) to test if I did not make some silly mistake in my algorithm and here the result (Works as expected):
const int p=10; // min periods for testing
const int n=500; // generated sequence size
int seq[n]; // generated sequence
int offset,period; // generated properties
int i,j,k,e,t0,T;
int hval[n],hcnt[n],hs; // histogram
// generate periodic sequence
Randomize();
offset=Random(n/5);
period=5+Random(n/5);
for (i=0;i<offset+period;i++) seq[i]=Random(n);
for (i=offset,j=i+period;j<n;i++,j++) seq[j]=seq[i];
if ((offset)&&(seq[offset-1]==seq[offset-1+period])) seq[offset-1]++;
// compute histogram O(n) on last half of it
for (hs=0,i=n>>1;i<n;i++)
{
for (e=seq[i],j=0;j<hs;j++)
if (hval[j]==e) { hcnt[j]++; j=-1; break; }
if (j>=0) { hval[hs]=e; hcnt[hs]=1; hs++; }
}
// bubble sort histogram asc O(m^2)
for (e=1,j=hs;e;j--)
for (e=0,i=1;i<j;i++)
if (hcnt[i-1]>hcnt[i])
{ e=hval[i-1]; hval[i-1]=hval[i]; hval[i]=e;
e=hcnt[i-1]; hcnt[i-1]=hcnt[i]; hcnt[i]=e; e=1; }
// test possible periods
for (j=0;j<hs;j++)
if ((!j)||(hcnt[j]!=hcnt[j-1])) // distinct counts only
if (hcnt[j]>1) // more then 1 occurence
for (T=(n>>1)/(hcnt[j]+1);T<=(n>>1)/(hcnt[j]-1);T++)
{
for (i=n-1,e=seq[i],i-=T,k=0;(i>=(n>>1))&&(k<p)&&(e==seq[i]);i-=T,k++);
if ((k>=p)||(i<n>>1)) { j=hs; break; }
}
// compute histogram O(T) on last multiple of period
for (hs=0,i=n-T;i<n;i++)
{
for (e=seq[i],j=0;j<hs;j++)
if (hval[j]==e) { hcnt[j]++; j=-1; break; }
if (j>=0) { hval[hs]=e; hcnt[hs]=1; hs++; }
}
// least count is the period multiple O(m)
for (e=hcnt[0],i=0;i<hs;i++) if (e>hcnt[i]) e=hcnt[i];
if (e) T/=e;
// check/handle error
if (T!=period)
{
return;
}
// search offset size O(n)
for (t0=-1,i=0;i<n-T;i++)
if (seq[i]!=seq[i+T]) t0=i;
t0++;
// check/handle error
if (t0!=offset)
{
return;
}
Code is still not optimized. For n=10000 it takes around 5ms on mine setup. The result is in t0 (offset) and T (period). You may need to play with the treshold constants a bit
I had to do something similar once. I used brute force and some common sense, the solution is not very elegant but it works. The solution always works, but you have to set the right parameters (k,j, con) in the function.
The sequence is saved as a list in the variable seq.
k is the size of the sequence array, if you think your sequence will take long to become periodic then set this k to a big number.
The variable found will tell us if the array passed the periodic test with period j
j is the period.
If you expect a huge period then you must set j to a big number.
We test the periodicity by checking the last j+30 numbers of the sequence.
The bigger the period (j) the more we must check.
As soon as one of the test is passed we exit the function and we return the smaller period.
As you may notice the accuracy depends on the variables j and k but if you set them to very big numbers it will always be correct.
def some_sequence(s0, a, b, m):
try:
seq=[s0]
snext=s0
findseq=True
k=0
while findseq:
snext= (a*snext+b)%m
seq.append(snext)
#UNTIL THIS PART IS JUST TO CREATE THE SEQUENCE (seq) SO IS NOT IMPORTANT
k=k+1
if k>20000:
# I IS OUR LIST INDEX
for i in range(1,len(seq)):
for j in range(1,1000):
found =True
for con in range(j+30):
#THE TRICK IS TO START FROM BEHIND
if not (seq[-i-con]==seq[-i-j-con]):
found = False
if found:
minT=j
findseq=False
return minT
except:
return None
simplified version
def get_min_period(sequence,max_period,test_numb):
seq=sequence
if max_period+test_numb > len(sequence):
print("max_period+test_numb cannot be bigger than the seq length")
return 1
for i in range(1,len(seq)):
for j in range(1,max_period):
found =True
for con in range(j+test_numb):
if not (seq[-i-con]==seq[-i-j-con]):
found = False
if found:
minT=j
return minT
Where max_period is the maximun period you want to look for, and test_numb is how many numbers of the sequence you want to test, the bigger the better but you have to make max_period+test_numb < len(sequence)

Algorithm for finding best top-scoring combinations

I am looking for an algorithm to identify combinations of game outcomes that maximize total points. The rules for the combinations
are as follows:
There are a total of N games
Each game has 3 possible outcomes (win, lose, or draw)
Each outcome has positive or negative score
Find the top outcome combinations of size N in order to maximize the total score
For example, with N = 2:
Game 1, Outcome 1 = +3 points
Game 1, Outcome 2 = -1 points
Game 1, Outcome 3 = -3 points
Game 2, Outcome 1 = -3 points
Game 2, Outcome 2 = +1 points
Game 2, Outcome 3 = +3 points
With these 2 games and point values for each possible outcome, here is the ordered list of combinations I would expect to see. Note that combinations 4, 5, and 6 are ties so they could have been in any order here.
Combination 1 = (Game 1, Outcome 1) + (Game 2, Outcome 3) -> Total +6 points
Combination 2 = (Game 1, Outcome 1) + (Game 2, Outcome 2) -> Total +4 points
Combination 3 = (Game 1, Outcome 2) + (Game 2, Outcome 3) -> Total +2 points
Combination 4 = (Game 1, Outcome 1) + (Game 2, Outcome 1) -> Total 0 points
Combination 5 = (Game 1, Outcome 2) + (Game 2, Outcome 2) -> Total 0 points
Combination 6 = (Game 1, Outcome 3) + (Game 2, Outcome 3) -> Total 0 points
Combination 7 = (Game 1, Outcome 3) + (Game 2, Outcome 2) -> Total -2 points
Combination 8 = (Game 1, Outcome 2) + (Game 2, Outcome 1) -> Total -4 points
Combination 9 = (Game 1, Outcome 3) + (Game 2, Outcome 1) -> Total -6 points
I can compute these ordered combinations by brute force for small values of N, but given that there are a total of 3^N combinations and that N can be as large as 128, I don't expect brute force to work for very long. So, I am looking for a way to identify the first M combinations, where M << the total number (3^N) of combinations.
I've spent quite a bit of time trying to come up with an algorithmic way to pick these combinations but I'm coming up short. I'd appreciate any suggestions that point me in the right direction.
Thanks
You can generate M combinations iteratively using a Priority Queue. First, you need to make a data structure that represents a single combination, along with a function to compute its score. For example, you could use an array of N small integers representing the number of the outcome. Use the score function to order the combinations inside the queue.
You will also need a way to quickly identify that you have seen a particular combination. For that your combination representation needs to have a hash function. Make a hash set of the combinations that you have explored.
It is trivial to compute the top combination: you can do it by grabbing the highest-value outcome for each of the N games. Add this combination to the priority queue, then run the following loop:
Dequeue the next best combination, and add it to the list of results.
If the length of the result list is M, you are done
Otherwise, go through the current combination, and produce up to 2N "derived" combinations from it
Each derived combination differs from the current combination in a single game outcome
Flip the outcome of each game from the best to the second best, then to the worst.
Check the combination with "flipped" outcomes against the hash set of the combinations you have explored
If this is a new combination, add it to the hash set and also to the priority queue
When you are done with the derived combinations, trim the queue down to M-res items, where res is the number of items on the results list
Continue to the next iteration.

loaded die algorithm

I want an algorithm to simulate this loaded die:
the probabilities are:
1: 1/18
2: 5/18
3: 1/18
4: 5/18
5: 1/18
6: 5/18
It favors even numbers.
My idea is to calculate in matlab the possibility of the above.
I can do it with 1/6 (normal die), but I am having difficulties applying it for a loaded die.
One way: generate two random numbers: first one is from 0 to 5 (0: odd, 1 - 5: even), which is used to determine even or odd. Then generate a second between 0 and 2, which determines exact number within its category. For example, if the first number is 3 (which says even) and second is 2 (which says the third chunk, 1-2 is a chunk, 3-4 is another chunk and 5-6 is the last chunk), the the result is 6.
Another way: generate a random number between 0 and 17, then you can simply / 6 and % 6 and use those two numbers to decide. For example, if /6 gives you 0, then the choice is between 1 and 2, then if % 6 == 0, the choice lands on 1, otherwise lands on 2.
In matlab:
ceil(rand*3)*2-(rand>(5/6))
The generic solution:
Use roulette wheel selection
n = generate number between 0 and sum( probabilities )
s = 0;
i = 0;
while s <= n do
i = i + 1;
s = s + probability of element i;
done
After the loop is done i will be the number of the chosen element. This works for any kind of skewed probability distribution, even when you have weights instead of a probability and want to skip normalizing.
In the concise language of J,
>:3(<([++:#])|)?18

Resources