Facebook Hacker Cup Subround 1B - Slot Machine Hacker - algorithm

Source: Facebook Hacker Cup.
I've tried generating a few lists of return values from the function below but can't seem to find what makes it possible to predict future random numbers. How would I go about solving a problem like this one?
Slot Machine Hacker
You recently befriended a guy who writes software for slot machines. After hanging out with him a bit, you notice that he has a penchant for showing off his knowledge of how the slot machines work. Eventually you get him to describe for you in precise detail the algorithm used on a particular brand of machine. The algorithm is as follows:
int getRandomNumber() {
secret = (secret * 5402147 + 54321) % 10000001;
return secret % 1000;
}
This function returns an integer number in [0, 999]; each digit represents one of ten symbols that appear on a wheel during a particular machine state.secret is initially set to some nonnegative value unknown to you.
By observing the operation of a machine long enough, you can determine value of secret and thus predict future outcomes. Knowing future outcomes you would be able to bet in a smart way and win lots of money.
Input
The first line of the input contains positive number T, the number of test cases. This is followed by T test cases. Each test case consists of a positive integer N, the number of observations you make. Next N tokens are integers from 0 to 999 describing your observations.
Output
For each test case, output the next 10 values that would be displayed by the machine separated by whitespace.
If the sequence you observed cannot be produced by the machine your friend described to you, print “Wrong machine” instead.
If you cannot uniquely determine the next 10 values, print “Not enough observations” instead.
Constraints
T = 20
1 ≤ N ≤ 100
Tokens in the input are no more than 3 characters long and contain only digits 0-9.
sample input
5
1 968
3 767 308 284
5 78 880 53 698 235
7 23 786 292 615 259 635 540
9 862 452 303 558 767 105 911 846 462
sample output
Not enough observations
577 428 402 291 252 544 735 545 771 34
762 18 98 703 456 676 621 291 488 332
38 802 434 531 725 594 86 921 607 35
Wrong machine

Got it!
Here is my solution in Python:
a = 5402147
b = 54321
n = 10000001
def psi(x):
return (a * x + b) % n
inverse1000 = 9990001
max_k = (n-1) / 1000 + 1
def first_input(y):
global last_input, i, possible_k
last_input = y
possible_k = [set(range(max_k))]
i = 0
def add_input(y):
global last_input, i
c = inverse1000 * (b + a * last_input - y) % n
sk0 = set()
sk1 = set()
for k0 in possible_k[i]:
ak0 = a * k0 % n
for k1 in range(max_k):
if (k1 - ak0) % n == c:
sk0.add(k0)
sk1.add(k1)
#print "found a solution"
last_input = y
possible_k[i] = possible_k[i] & sk0
possible_k.append(sk1)
i += 1
if len(possible_k[i-1]) == 0 or len(possible_k[i]) == 0:
print "Wrong machine"
return
if len(possible_k[i]) == 1:
x = y + 1000 * possible_k[i].copy().pop()
for j in range(10):
x = psi(x)
print x % 1000,
print
return
print "Not enough observations"
It could probably be optimized (and cleaned), but since it runs in less than 30sec on my 3 years old laptop I probably won't bother making it faster...
The program doesn't accept exactly the same input as requested, here is how to use it:
>>> first_input(767)
>>> add_input(308)
Not enough observations
>>> add_input(284)
577 428 402 291 252 544 735 545 771 34
>>> first_input(78)
>>> add_input(880)
Not enough observations
>>> add_input(53)
698 235 762 18 98 703 456 676 621 291
>>> add_input(698)
235 762 18 98 703 456 676 621 291 488
>>> add_input(235)
762 18 98 703 456 676 621 291 488 332
>>> first_input(862)
>>> add_input(452)
Not enough observations
>>> add_input(303)
Wrong machine
>>> add_input(558)
Wrong machine
As you can see, usually 3 observations are enough to determine the future outcomes.
Since it's a pain to write math stuff in a text editor I took a picture of my demonstration explanation:

secret is always between 0 and 10,000,000 inclusive due to the mod by 10,000,001. The observed values are always the last 3 digits of secret (with leading zeros stripped) due to the mod by 1,000. So it's the other digits that are unknown and that only leaves us with 10,001 numbers to iterate over.
For each prefix in 0..10,000, we start with secret being constructed from the digits of prefix followed by the first number in the observed sequence with leading zeros. If the list of generated numbers equals the observed list, we have a potential seed. If we end with no potential seeds, we know this must be a wrong machine. If we end with more than one, we don't have enough observations. Otherwise, we generate the next 10 values using the single seed.
This runs in O(10,000NT), which is O(20,000,000) for the constraints given. Here's an extract from my solution in C++ (excuse the heavy use of macros, I only ever use them in competitions):
int N;
cin >> N;
int n[N];
REP(i, N)
cin >> n[i];
ll poss = 0, seed = -1;
FOR(prefix, 0, 10001) {
ll num = prefix * 1000 + n[0];
bool ok = true;
FOR(i, 1, N) {
ll next = getRandomNumber(num);
if (next != n[i]) {
ok = false;
break;
}
}
if (ok) {
poss++;
seed = prefix * 1000 + n[0];
}
}
if (poss == 0) {
cout << "Wrong machine" << endl;
} else if (poss > 1) {
cout << "Not enough observations" << endl;
} else {
ll num = seed;
FOR(i, 1, N)
getRandomNumber(num);
REP(i, 10)
cout << getRandomNumber(num) << " ";
cout << endl;
}

What is known here? The formula for updating the secret, and the list of observations. What is
unknown? The starting secret value.
What could the starting secret be? We can limit the possible starting
secret to 10,000 possible values, since the observed value is secret
% 1000, and the maximum secret is 10,000,000.
The possible starting secrets are then
possible = [1000 * x + observed for x in xrange(10001)]
Only a subset (if any) of those secrets will update to a value that will show the next
observed value.
def f(secret):
return (secret * 5402147 + 54321) % 10000001
# obs is the second observation.
new_possible = [f(x) for x in possible]
new_possible = [x for x in new_possible if x % 1000 == obs]
Even if every possible value was still in new_possible, we would only be checking 10,000
numbers for every observation. But, it is not likely that many values will match over
multiple observations.
Just keep iterating that process, and either the possible list will be empty, longer than one,
or it will have exactly one answer.
Here is a function to put it all together. (you need the f from above)
def go(observations):
if not observations:
return "not enough observations"
# possible is the set of all possible secret states.
possible = [x * 1000 + observations[0] for x in xrange(10001)]
# for each observation after the first, cull the list of possible
# secret states.
for obs in observations[1:]:
possible = [f(x) for x in possible]
possible = [x for x in possible if x % 1000 == obs]
# uncomment to see the possible states as the function works
# print possible
# Either there is 0, 1, or many possible states at the end.
if not possible:
return "wrong machine"
if len(possible) > 1:
return "not enough observations"
secret = possible[0]
nums = []
for k in xrange(10):
secret = f(secret)
nums.append(str(secret % 1000))
return " ".join(nums)
import sys
def main():
num_cases = int(sys.stdin.readline())
for k in xrange(num_cases):
line = [int(x) for x in sys.stdin.readline().split()]
print go(line[1:])
main()

Related

all zero one sequences where ones aren't next to each other

I've got to write a procedure which for every given n>0 writes down every single 0-1 sequence of lenght n, where 1 can't sit next to any other 1.
For example: for n=3 the output is:
000, 001, 010, 100, 101
I thought about recursion but can't see any use of it in this case.
Any hints will be highly appreciated.
Since you asked for a hint and you mentioned recursion, here's a recursive hint:
0 -> can precede either 1 or 0
-> 01
-> 00
1 -> can precede 0
-> 10
01 -> 010
00 -> 000
001
10 -> 100
101
Just exclude the result, which have consecutive 1s .
Below is the sample java Code :
public class Test0 {
public static void main (String [] args) {
int n = 4;
for(int i = 0; i < Math.pow(2,n); i++) {
if ( (i & (i << 1)) != 0 )
continue;
System.out.println( i/8 % 2 + "" + i/4 % 2 + "" + i/2 % 2 + "" + i % 2);
}
}
}
If there is two consecutiv 1s, expression i & (i << 1)) will result in non-zero, otherwise 0.
Here is a Python recursive solution:
def rec(n,a=''):
if len(a)==n:
print a
return
rec(n,a+'0')
if len(a)==0 or a[-1]!='1':
rec(n,a+'1')
rec(3)
prints:
000
001
010
100
101
It actually turns out by Zeckendorf's theorem that the n^th term in this sequence is equal to the Zeckendorf representation (which expresses numbers in terms of Fibonacci terms) of the number n. This explains why the number of choices is given by the Fibonacci numbers.

3n+1 Optimization Idea for Larger Integers

I recently got into the book "Programming Challenges" by Skiena and Revilla and was somewhat surprised when I saw the solution to the 3n+1 problem, which was simply brute forced. Basically it's an algorithm that generates a list of numbers, dividing by 2 if even and multiplying by 3 and adding 1 if odd. This occurs until n=1 is reached, its base case. Now the trick is to find the maximum length of a list between integers i and j which in the problem ranges between 1 and 1,000,000 for both variables. So I was wondering how much more efficient (if so) a program would be with Dynamic Programming. Basically, the program would do one pass on the first number, i, find the total length, and then check each individual number within the array and store the associated lengths within a HashMap or other dictionary data type.
For Example:
Let's say i = 22 and j = 23
For 22:
22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
This means that in the dictionary, with the structure would store
(22,16) , (11,15) , (34,14) and so on... until (1,1)
Now for 23:
23 70 35 106 53 160 80 40 ...
Since 40 was hit, and it is in the dictionary
program would get the length of 23 to 80, which is 7, and add it to the length stored previously by 40 which is 9 resulting in total list length of 16. And of course the program would store lengths of 23, 70 , 35 etc... such that if the numbers were bigger it should compute faster.
So what are the opinions of approaching such a question in this manner?
I tried both approaches and submitted them to UVaOJ, the brute force solution got runtime ~0.3s and the dp solution ~0.0s. It gets pretty slow when the range gets long (like over 1e7 elements).
I just used an array (memo) to be able to memorize the first 5 million (SIZE) values:
int cycleLength(long long n)
{
if(n < 1) //overflow
return 0;
if (n == 1)
return 1;
if (n < SIZE && memo[n] != 0)
return memo[n];
int res = 1 + cycleLength(n % 2 == 0 ? n / 2 : 3 * n + 1);
if (n < SIZE)
memo[n] = res;
return res;
}

How to search the minimum n that 10^n ≡ 1 mod(9x) for given x

For given x, I need to calculate the minimum n that equates true for the formula 10^n ≡ 1 (mod 9x)
My algorithm is simple. For i = 1 to inf, I loop it until I get a result. There is always a result if gcd(10, x) = 1. Meanwhile if I don't get a result, I increase i by 1 .
This is really slow for big primes or numbers with a factorization of big values, so I ask if there is another way to calculate it faster. I have tried with threads, getting each thread the next 10^i to calculate. Performance is a bit better, but big primes still don't finish.
You can use Fermat's Little Theorem.
Assuming your x is relatively prime with 10, the following holds:
10 ^ φ(9x) ≡ 1 (mod 9x)
Here φ is Euler's totient function. So you can easily calculate at least one n (not necessarily the smallest) for which your equation holds. To find the smallest such n, just iterate through the list of n's divisors.
Example: x = 89 (a prime number just for simplicity).
9x = 801
φ(9x) = 6 * (89 - 1) = 528 (easy to calculate for a prime number)
The list of divisors of 528:
1
2
3
4
6
8
11
12
16
22
24
33
44
48
66
88
132
176
264
528
Trying each one, you can find that your equation holds for 44:
10 ^ 44 ≡ 1 (mod 801)
I just tried the example, it runs in less than one second:
public class Main {
public static void main(String[] args) {
int n = 1;
int x = 954661;
int v = 10;
while (v != 1) {
n++;
v = (v * 10) % (9*x);
}
System.out.println(n);
}
}
For larger values of x the variables should be of type long.
As you specified you are actually trying to get modulus with 1 that is 1mod(9x).
That will always give you 1.
And you don't have to calculate that part exactly which might reduce your calculation.
On the other hand for 10^n = 1, it will always be 0.
So can you exactly specify what you are trying to do

How many times the function will be invoked?

I have this cycle:
for(i = 0; i < n; i ++) {
if(i % 5 == 1 && i % 3 == 1) {
function();
}
}
How can i count amount of calls of function() without running this code?
I take from the complexity-theory tag that you want some Theta expression. The if causes your function to be executed every fifteenth time, which is a constant factor, so the number of executions is still Theta(n).
The conditional has two expressions. The first expression holds true every 5 iterations and the second holds true every 3 iterations. Together they hold true apprx every 15 rounds and function() gets called.
Look at the values of i where your condition holds:
1
16
31
46
61
76
91
106
121
136
151
166
181
196
...
Now, what would be the case if you had the condition i % 5 == 0 && i % 3 == 0, it would have to be a multiple of 15 (lcm(3,5)) and then the condition would hold on every 15th iteration. From that you can likely derive the relation yourself.
The if statement is true when i = 15*k +1 where k is whole number. For total number like that within (0,n) is given by k = floor((n-1)/15) + 1 for example n = 31, k = floor((31-1)/15) + 1 = 3 which is (1,16,31)

Xnary (like binary but different) counting

I'm making a function that converts a number into a string with predefined characters. Original, I know. I started it, because it seemed fun at the time. To do on my own. Well, it's frustrating and not fun.
I want it to be like binary as in that any left character is worth more than its right neigbour. Binary is inefficient because every bit has only 1 positive value. Xnary is efficient, because a 'bit' is never 0.
The character set (in this case): A - Z.
A = 1 ..
Z = 26
AA = 27 ..
AZ = 52
BA = 53 ..
BZ = 2 * 26 (B) + 26 * 1 (Z) = 78... Right?
ZZ = 26 * 26 (Z) + 26 * 1 (Z) = 702?? Right??
I found this here, but there AA is the same as A and AAA. The result of the function is never AA or AAA.
The string A is different from AA and AAA however, so the number should be too. (Unlike binary 1, 01, 001 etc.) And since a longer string is always more valuable than a shorter... A < AA < AAA.
Does this make sense? I've tried to explain it before and have failed. I've also tried to make it before. =)
The most important thing: since A < AA < AAA, the value of 'my' ABC is higher than the value of the other script. Another difference: my script doesn't exist, because I keep failing.
I've tried with this algorithm:
N = 1000, Size = 3, (because 26 log(1000) = 2.x), so use 676, 26 and 1 for positions:
N = 1000
P0 = 1000 / 676 = 1.x = 1 = A
N = 1000 - 1 * 676 = 324
P1 = 324 / 26 = 12.x = 12 = L
N = 324 - 12 * 26 = 12
P1 = 12 / 1 = 12 = L
1000 => ALL
Sounds fair? Apparently it's crap. Because:
N = 158760, Size = 4, so use 17576, 676, 26 and 1
P0 = 158760 / 17576 = 9.x = 9 = I
N = 158760 - 9 * 17576 = 576
P1 = 576 / 676 = 0.x = 0 <<< OOPS
If 1 is A (the very first of the xnary), what's 0? Impossible is what it is.
So this one is a bust. The other one (on jsFiddle) is also a bust, because A != AA != AAA and that's a fact.
So what have I been missing for a few long nights?
Oh BTW: if you don't like numbers, don't read this.
PS. I've tried searching for similar questions but none are similar enough. The one references is most similar, but 'faulty' IMO.
Also known as Excel column numbering. It's easier if we shift by one, A = 0, ..., Z = 25, AA = 26, ..., at least for the calculations. For your scheme, all that's needed then is a subtraction of 1 before converting to Xnary resp. an addition after converting from.
So, with that modification, let's start finding the conversion. First, how many symbols do we need to encode n? Well, there are 26 one-digit numbers, 26^2 two-digit numbers, 26^3 three-digit numbers etc. So the total of numbers using at most d digits is 26^1 + 26^2 + ... + 26^d. That is the start of a geometric series, we know a closed form for the sum, 26*(26^d - 1)/(26-1). So to encode n, we need d digits if
26*(26^(d-1)-1)/25 <= n < 26*(26^d-1)/25 // remember, A = 0 takes one 'digit'
or
26^(d-1) <= (25*n)/26 + 1 < 26^d
That is, we need d(n) = floor(log_26(25*n/26+1)) + 1 digits to encode n >= 0. Now we must subtract the total of numbers needing at most d(n) - 1 digits to find the position of n in the d(n)-digit numbers, let's call it p(n) = n - 26*(26^(d(n)-1)-1)/25. And the encoding of n is then simply a d(n)-digit base-26 encoding of p(n).
The conversion in the other direction is then a base-26 expansion followed by an addition of 26*(26^(d-1) - 1)/25.
So for N = 1000, we encode n = 999, log_26(25*999/26+1) = log_26(961.5769...) = 2.x, we need 3 digits.
p(999) = 999 - 702 = 297
297 = 0*26^2 + 11*26 + 11
999 = ALL
For N = 158760, n = 158759 and log_26(25*158759/26+1) = 3.66..., we need four digits
p(158759) = 158759 - 18278 = 140481
140481 = 7*26^3 + 25*26^2 + 21*26 + 3
158759 = H Z V D
This appears to be a very standard "implement conversion from base 10 to base N" where N happens to be 26, and you're using letters to represent all digits.
If you have A-Z as a 26ary value, you can represent 0 through (26 - 1) (like binary can represent 0 - (2 - 1).
BZ = 1 * 26 + 25 *1 = 51
The analogue would be:
19 = 1 * 10 + 9 * 1 (1/B being the first non-zero character, and 9/Z being the largest digit possible).
You basically have the right idea, but you need to shift it so A = 0, not A = 1. Then everything should work relatively sanely.
In the lengthy answer by #Daniel I see a call to log() which is a red flag for performance. Here is a simple way without much complex math:
function excelize(colNum) {
var order = 0, sub = 0, divTmp = colNum;
do {
divTmp -= 26**order;
sub += 26**order;
divTmp = (divTmp - (divTmp % 26)) / 26;
order++;
} while(divTmp > 0);
var symbols = "0123456789abcdefghijklmnopqrstuvwxyz";
var tr = c => symbols[symbols.indexOf(c)+10];
Number(colNum-sub).toString(26).split('').map(c=>tr(c)).join('');
}
Explanation:
Since this is not base26, we need to substract the base times order for each additional symbol ("digit"). So first we count the order of the resulting number, and at the same time count the substract. And then we convert it to base 26 and substract that, and then shift the symbols to A-Z instead of 0-P.

Resources