Related
I encountered the following algorithmic question which has strict constraints on runtime (<10s and no large memory footprint) and I am stumped. My approach fails half the test cases.
Question
A box contains a number of items that can only br removed 1 at a time or 3 at a time.
How many ways can the box be emptied? the answer can be very large so return it as modulo of 10^9+7.
for example,there are n=7 items initially.They can be removed nine ways,as follows:
1.(1,1,1,1,1,1,1)
2.(1.1.1.1.3)
3.(1,1,1,3,1)
4.(1,1,3,1,1)
5.(1,3,1,1,1)
6.(3,1,1,1,1)
7.(1,3,3)
8.(3,1,3)
9.(3,3,1)
So the function should return 9.
Function Description:
Your function must take in a parameter, n for the number of items, and return an integer which denotes the number of ways to empty the box.
Constraints: 1<=n<=10^8
Sample cases :
Input: 1
Sample OutPut: 1
Explanation: There is only 1 way to remove 1 item. Answer=(1%1000000007)=1
Input: 7
Sample OutPut: 9
There is only 9 ways to remove 7 items
My Approach
This leads to a standard recurrence relation where f(n) = f(n-3) + f(n-1) for n > 2, so i did it as follows
def memoized_number_of_ways(dic, n):
if n not in dic:
dic[n] = memoized_number_of_ways(dic, n-3) + memoized_number_of_ways(dic, n-1)
return dic[n]
def numberOfWays(n):
# Write your code here
memoize = {1:1,2:1,3:2}
import math
ans = memoized_number_of_ways(memoize,n)
return ans % (math.pow(10,9) + 7)
However this fails on any case where n > 10**2. How can you do this problem while accomodating n up to 10^8 and in less than 10s with not much memory?
Just write your recurrence using matrices (pardon my way of writing matrices, StackOverflow doesn't allow LaTeX).
[f(n) ] = [1 0 1] [f(n-1) ]
[f(n-1)] = [1 0 0] [f(n-2) ]
[f(n-2)] = [0 1 0] [f(n-3) ]
Now all you have to do is raise a 3x3 matrix (modulo fixed constant) to power n (or n-3 or something like that, depending on your "base case column vector", fill in the details), and then multiply it by a "base case column vector". This can be done in time O(logn).
PS: You may want to lookup matrix exponentiation.
Three solutions, fastest takes about 31 μs for n=108 on tio.run (which has medium-fast computers).
A matrix power solution like described by advocateofnone that takes about 1 millisecond (Try it online!):
import numpy as np
from time import time
class ModInt:
def __init__(self, x):
self.x = x % (10**9 + 7)
def __add__(a, b):
return ModInt(a.x + b.x)
def __mul__(a, b):
return ModInt(a.x * b.x)
def __str__(self):
return str(self.x)
def solve(n):
O = ModInt(0)
I = ModInt(1)
A = np.matrix([[O,I,O], [O,O,I], [I,O,I]])
return (A**n)[2,2]
for _ in range(3):
t0 = time()
print(solve(10**8), time() - t0)
Output (result and time in seconds for n=108, three attempts):
109786077 0.0010712146759033203
109786077 0.0010180473327636719
109786077 0.0009677410125732422
Another, taking about 0.5 milliseconds (Try it online!):
import numpy as np
from time import time
def solve(n):
A = np.matrix([[0,1,0], [0,0,1], [1,0,1]])
power = 1
mod = 10**9 + 7
while n:
if n % 2:
power = power * A % mod
A = A**2 % mod
n //= 2
return power[2,2]
for _ in range(3):
t0 = time()
print(solve(10**8), time() - t0)
One based on #rici's solution in the comments, takes about 31 μs (Try it online!):
from timeit import repeat
def solve(n):
m = 10**9 + 7
def abc(n):
if n == 0:
return 0, 1, 0
a, b, c = abc(n // 2)
d = a + c
e = b + d
A = 2*a*b + c*c
C = 2*b*c + d*d
E = 2*c*d + e*e
D = A + C
B = E - D
if n % 2:
A, B, C = B, C, D
return A%m, B%m, C%m
return sum(abc(n)) % m
n = 10**8
print(solve(n))
for _ in range(3):
t = min(repeat(lambda: solve(n), 'gc.enable()', number=1000)) / 1000
print('%.1f μs' % (t * 1e6))
Explanation: Looking at the matrix powers from my previous solutions, I noticed they only actually contain five different values, and they're consecutive result numbers from our desired sequence. For example, A**19 is:
[[277 189 406]
[406 277 595]
[595 406 872]]
I gave them names in increasing order:
| b a c |
| c b d |
| d c e |
Squaring that matrix results in a matrix for larger n, with entries A/B/C/D/E. And if you square the above matrix, you'll find the relationships A = 2*a*b + c*c etc.
My helper function abc(n) computes the entries a/b/c of the n-th matrix power. For n=0, that's the identity matrix, so my a/b/c are 0/1/0 there. And in the end I, return the e-value (computed as e=b+d=a+b+c).
Here's a simple iterative O(n) time / O(1) space solution whose optimized version takes 6 seconds on a medium-fast machine (unoptimized takes 15 seconds there).
Unoptimized (Try it online!):
def solve(n):
mod = 10**9 + 7
a = b = c = 1
for _ in range(n):
a, b, c = b, c, (a+c) % mod
return a
print(solve(7))
print(solve(10**8))
Optimized (Try it online!):
def solve(n):
mod = 10**9 + 7
a = b = c = 1
for _ in range(n // 300):
for _ in range(100):
a += c
b += a
c += b
a %= mod
b %= mod
c %= mod
for _ in range(n % 300):
a, b, c = b, c, (a+c) % mod
return a
Your solution is on the right track and the bug is not related to your algorithm (Yay).
The problem is when you are performing operations on some big numbers you lose precision. Notice that you can apply the mod 10 ** 9 + 7 along your code since addition is not affected by it. By doing so you keep all your numbers below a certain size and you will not have any floating point precision errors:
import math
def memoized_number_of_ways(dic, n):
if n not in dic:
dic[n] = (memoized_number_of_ways(dic, n-3) + memoized_number_of_ways(dic, n-1)) % (math.pow(10,9) + 7)
return dic[n]
def numberOfWays(n):
memoize = {1:1,2:1,3:2}
ans = memoized_number_of_ways(memoize,n)
return ans
Note that for you to be able to answer the question for n > 1000 you need to solve this recursion error problem.
Unfortunately even a very efficient solution (hint: you don't really need more than 3 items in your dict at any moment) will not solve the question for n ~ 10 ** 9 under a second. And you will need to find another way - a great option is the second answer here :)
One of my friends got this question in google coding contest. Here goes the question.
Find the number of N-digit numbers that are divisible by both X and Y.
Since the answer can be very large, print the answer modulo 10^9 + 7.
Note: 0 is not considered single-digit number.
Input: N, X, Y.
Constraints:
1 <= N <= 10000
1 <= X,Y <= 20
Eg-1 :
N = 2, X = 5, Y = 7
output : 2 (35 and 70 are the required numbers)
Eg-2 :
N = 1, X = 2, Y = 3
output : 1 (6 is the required number)
If the constraints on N were smaller, then it would be easy (ans = 10^N / LCM(X,Y) - 10^(N-1) / LCM(X,Y)).
But N is upto 1000, hence I am unable to solve it.
This question looks like it was intended to be more difficult, but I would do it pretty much the way you said:
ans = floor((10N-1)/LCM(X,Y)) - floor((10N-1-1)/LCM(X,Y))
The trick is to calculate the terms quickly.
Let M = LCM(X,Y), and say we have:
10a = Mqa + ra, and
10b = Mqb + rb
The we can easily calculate:
10a+b = M(Mqaqb + raqb + rbqa + floor(rarb/M)) + (rarb%M)
With that formula, we can calculate the quotient and remainder for 10N/M in just 2 log N steps using exponentiation by squaring: https://en.wikipedia.org/wiki/Exponentiation_by_squaring
Following python works for this question ,
import math
MOD = 1000000007
def sub(x,y):
return (x-y+MOD)%MOD
def mul(x,y):
return (x*y)%MOD
def power(x,y):
res = 1
x%=MOD
while y!=0:
if y&1 :
res = mul(res,x)
y>>=1
x = mul(x,x)
return res
def mod_inv(n):
return power(n,MOD-2)
x,y = [int(i) for i in input().split()]
m = math.lcm(x,y)
n = int(input())
a = -1
b = -1
total = 1
for i in range(n-1):
total = (total * 10)%m
b = total % m
total = (total*10)%m
a = total % m
l = power(10 , n-1)
r = power(10 , n)
ans = sub( sub(r , l) , sub(a,b) )
ans = mul(ans , mod_inv(m))
print(ans)
Approach for this question is pretty straight forward,
let, m = lcm(x,y)
let,
10^n -1 = m*x + a
10^(n-1) -1 = m*y + b
now from above two equations it is clear that our answer is equal to
(x - y)%MOD .
so,
(x-y) = ((10^n - 10^(n-1)) - (a-b)) / m
also , a = (10^n)%m and b = (10^(n-1))%m
using simple modular arithmetic rules we can easily calculate a and b in O(n) time.
also for subtraction and division performed in the formula we can use modular subtraction and division respectively.
Note: (a/b)%MOD = ( a * (mod_inverse(b, MOD)%MOD )%MOD
I have been working on a Hackerearth Problem. Here is the problem statement:
We have three variables a, b and c. We need to convert a to b and following operations are allowed:
1. Can decrement by 1.
2. Can decrement by 2.
3. Can multiply by c.
Minimum steps required to convert a to b.
Here is the algorithm I came up with:
Increment count to 0.
Loop through till a === b:
1. Perform (x = a * c), (y = a - 1) and (z = a - 2).
2. Among x, y and z, choose the one whose absolute difference with b is the least.
3. Update the value of a to the value chosen among x, y and z.
4. Increment the count by 1.
I can get pass the basic test case but all my advance cases are failing. I guess my logic is correct but due to the complexity it seems to fail.
Can someone suggest a more optimized solution.
Edit 1
Sample Code
function findMinStep(arr) {
let a = parseInt(arr[0]);
let b = parseInt(arr[1]);
let c = parseInt(arr[2]);
let numOfSteps = 0;
while(a !== b) {
let multiply = Math.abs(b - (a * c));
let decrement = Math.abs(b - (a - 1));
let doubleDecrement = Math.abs(b - (a - 2));
let abs = Math.min(multiply, decrement, doubleDecrement);
if(abs === multiply) a = a * c;
else if(abs === decrement) a -= 1;
else a -= 2;
numOfSteps += 1;
}
return numOfSteps.toString()
}
Sample Input: a = 3, b = 10, c = 2
Explanation: Multiply 3 with 2 to get 6, subtract 1 from 6 to get 5, multiply 5 with 2 to get 10.
Reason for tagging both Python and JS: Comfortable with both but I am not looking for code, just an optimized algorithm and analytical thinking.
Edit 2:
function findMinStep(arr) {
let a = parseInt(arr[0]);
let b = parseInt(arr[1]);
let c = parseInt(arr[2]);
let depth = 0;
let queue = [a, 'flag'];
if(a === b ) return 0
if(a > b) {
let output = Math.floor((a - b) / 2);
if((a - b) % 2) return output + 1;
return output
}
while(true) {
let current = queue.shift();
if(current === 'flag') {
depth += 1;
queue.push('flag');
continue;
}
let multiple = current * c;
let decrement = current - 1;
let doubleDecrement = current -2;
if (multiple !== b) queue.push(multiple);
else return depth + 1
if (decrement !== b) queue.push(decrement);
else return depth + 1
if (doubleDecrement !== b) queue.push(doubleDecrement);
else return depth + 1
}
}
Still times out. Any more suggestions?
Link for the question for you reference.
BFS
A greedy approach won't work here.
However it is already on the right track. Consider the graph G, where each node represents a value and each edge represents one of the operations and connects two values that are related by that operation (e.g.: 4 and 3 are connected by "subtract 1"). Using this graph, we can easily perform a BFS-search to find the shortest path:
def a_to_b(a, b, c):
visited = set()
state = {a}
depth = 0
while b not in state:
visited |= state
state = {v - 1 for v in state if v - 1 not in visited} | \
{v - 2 for v in state if v - 2 not in visited} | \
{v * c for v in state if v * c not in visited}
depth += 1
return 1
This query systematically tests all possible combinations of operations until it reaches b by testing stepwise. I.e. generate all values that can be reached with a single operation from a, then test all values that can be reached with two operations, etc., until b is among the generated values.
In depth analysis
(Assuming c >= 0, but can be generalized)
So far for the standard-approach that works with little analysis. This approach has the advantage that it works for any problem of this kind and is easy to implement. However it isn't very efficient and will reach it's limits fairly fast, once the numbers grow. So instead I'll show a way to analyze the problem in depth and gain a (far) more performant solution:
In a first step this answer will analyze the problem:
We need operations -->op such that a -->op b and -->op is a sequence of
subtract 1
subtract 2
multiply by c
First of all, what happens if we first subtract and afterwards multiply?
(a - x) * c = a * c - x * c
Next what happens, if we first multiply and afterwards subtract?
a * c - x'
Positional systems
Well, there's no simplifying transformation for this. But we've got the basic pieces to analyze more complicated chains of operations. Let's see what happens when we chain subtractions and multiplications alternatingly:
(((a - x) * c - x') * c - x'') * c - x'''=
((a * c - x * c - x') * c - x'') * c - x''' =
(a * c^2 - x * c^2 - x' * c - x'') * c - x''' =
a * c^3 - x * c^3 - x' * c^2 - x'' * c - x'''
Looks familiar? We're one step away from defining the difference between a and b in a positional system base c:
a * c^3 - x * c^3 - x' * c^2 - x'' * c - x''' = b
x * c^3 + x' * c^2 + x'' * c + x''' = a * c^3 - b
Unfortunately the above is still not quite what we need. All we can tell is that the LHS of the equation will always be >=0. In general, we first need to derive the proper exponent n (3 in the above example), s.t. it is minimal, nonnegative and a * c^n - b >= 0. Solving this for the individual coefficients (x, x', ...), where all coefficients are non-negative is a fairly trivial task.
We can show two things from the above:
if a < b and a < 0, there is no solution
solving as above and transforming all coefficients into the appropriate operations leads to the optimal solution
Proof of optimality
The second statement above can be proven by induction over n.
n = 0: In this case a - b < c, so there is only one -->op
n + 1: let d = a * c^(n + 1) - b. Let d' = d - m * c^(n + 1), where m is chosen, such that d' is minimal and nonnegative. Per induction-hypothesis d' can be generated optimally via a positional system. Leaving a difference of exactly m * c^n. This difference can not be covered more efficiently via lower-order terms than by m / 2 subtractions.
Algorithm (The TLDR-part)
Consider a * c^n - b as a number base c and try to find it's digits. The final number should have n + 1 digits, where each digit represents a certain number of subtractions. Multiple subtractions are represented by a single digit by addition of the subtracted values. E.g. 5 means -2 -2 -1. Working from the most significant to the least significant digit, the algorithm operates as follows:
perform the subtractions as specified by the digit
if the current digit is was the last, terminate
multiply by c and repeat from 1. with the next digit
E.g.:
a = 3, b = 10, c = 2
choose n = 2
a * c^n - b = 3 * 4 - 10 = 2
2 in binary is 010
steps performed: 3 - 0 = 3, 3 * 2 = 6, 6 - 1 = 5, 5 * 2 = 10
or
a = 2, b = 25, c = 6
choose n = 2
a * c^n - b = 47
47 base 6 is 115
steps performed: 2 - 1 = 1, 1 * 6 = 6, 6 - 1 = 5, 5 * 6 = 30, 30 - 2 - 2 - 1 = 25
in python:
def a_to_b(a, b, c):
# calculate n
n = 0
pow_c = 1
while a * pow_c - b < 0:
n += 1
pow_c *= 1
# calculate coefficients
d = a * pow_c - b
coeff = []
for i in range(0, n + 1):
coeff.append(d // pow_c) # calculate x and append to terms
d %= pow_c # remainder after eliminating ith term
pow_c //= c
# sum up subtractions and multiplications as defined by the coefficients
return n + sum(c // 2 + c % 2 for c in coeff)
I'm trying to convert a base-10 integer k into a base-q integer, but not in the standard way. Firstly, I'd like my result to be a vectors (or a string 'a,b,c,...' so that it can be converted to a vector, but not 'abc...'). Most importantly, I'd like each 'digit' to be in base-10. As an example, suppose I have the number 23 (in base-10) and I want to convert it to base-12. This would be 1B in the standard 1,...,9,A,B notation; however, I want it to come out as [1, 11]. I'm only interested in numbers k with 0 \le k \le n^q - 1, where n is fixed in advance.
Put another way, I wish to find coefficients a(r) such that
k = \sum_{r=0}^{n-1} a(r) q^r
where each a(r) is in base-10. (Note that 0 \le a(r) \le q-1.)
I know I could do this with a for-loop -- struggling to get the exact formula at the moment! -- but I want to do it vectorised, or with a fast internal function.
However, I want to be able to take n to be large, so would prefer a faster way than this. (Of course, I could change this to a parfor-loop or do it on the GPU; these aren't practical for my current situation, so I'd prefer a more direct version.)
I've looked at stuff like dec2base, num2str, str2num, base2dec and so on, but with no luck. Any suggestion would be most appreciated.
Regarding speed and space, any preallocation for integers in the range [0, q-1] or similar would also be good.
To be clear, I am looking for an algorithm that works for any q and n, converting any number in the range [0,q^n - 1].
You can use dec2base and replace the characters by numbers:
x = 23;
b = 12;
[~, result] = ismember(dec2base(x,b), ['0':'9' 'A':'Z']);
result = result -1;
gives
>> result
result =
1 11
This works for base up to 36 only, due to dec2base limitations.
For any base (possibly above 36) you need to do the conversion manually. I once wrote a base2base function to do that (it's essentially long division). The number should be input as a vector of digits in the origin base, so you need dec2base(...,10) first. For example:
x = 125;
b = 6;
result = base2base(dec2base(x,10), '0':'9', b); % origin nunber, origin base, target base
gives
result =
3 2 5
Or if you need to specify the number of digits:
x = 125;
b = 6;
d = 5;
result = base2base(dec2base(x,10), '0':'9', b, d)
result =
0 0 3 2 5
EDIT (August 15, 2017): Corrected two bugs: handling of input consisting of all "zeros" (thanks to #Sanchises for noticing), and properly left-padding the output with "zeros" if needed.
function Z = base2base(varargin)
% Three inputs: origin array, origin base, target base
% If a base is specified by a number, say b, the digits are [0,1,...,d-1].
% The base can also be directly an array with the digits
% Fourth input, optional: how many digits the output should have as a
% minimum (padding with leading zeros, i.e with the first digit)
% Non-valid digits in origin array are discarded.
% It works with cell arrays. In this case it gives a matrix in which each
% row is padded with leading zeros if needed
% If the base is specified as a number, digits are numbers, not
% characters as in `dec2base` and `base2dec`
if ~iscell(varargin{1}), varargin{1} = varargin(1); end
if numel(varargin{2})>1, ax = varargin{2}; bx=numel(ax); else bx = varargin{2}; ax = 0:bx-1; end
if numel(varargin{3})>1, az = varargin{3}; bz=numel(az); else bz = varargin{3}; az = 0:bz-1; end
Z = cell(size(varargin{1}));
for c = 1:numel(varargin{1})
x = varargin{1}{c}; [valid, x] = ismember(x,ax); x = x(valid)-1;
if ~isempty(x) && ~any(x) % Non-empty input, all zeros
z = 0;
elseif ~isempty(x) % Non-empty input, at least a nonzero
z = NaN(1,ceil(numel(x)*log2(bx)/log2(bz))); done_outer = false;
n = 0;
while ~done_outer
n = n + 1;
x = [0 x(find(x,1):end)];
y = NaN(size(x)); done_inner = false;
m = 0;
while ~done_inner
m = m + 1;
t = x(1)*bx+x(2);
r = mod(t, bz); q = (t-r)/bz;
y(m) = q; x = [r x(3:end)];
done_inner = numel(x) < 2;
end
y = y(1:m);
z(n) = r; x = y; done_outer = ~any(x);
end
z = z(n:-1:1);
else % Empty input
z = []; % output will be empty (unless user has required left-padding) with the
% appropriate class
end
if numel(varargin)>=4 && numel(z)<varargin{4}, z = [zeros(1,varargin{4}-numel(z)) z]; end
% left-pad if required by user
Z{c} = z;
end
L = max(cellfun(#numel, Z));
Z = cellfun(#(x) [zeros(1, L-numel(x)) x], Z, 'uniformoutput', false); % left-pad so that
% result will be a matrix
Z = vertcat(Z{:});
Z = az(Z+1);
Matlab's internal dec2base command contains essentially what you are asking for.
It actually creates an array of base-10 digits before they are converted to a character array of '0'-'9' and 'A'-'Z' which is the reason for its limitation to bases <= 36.
So after removing the last step of character conversion from dec2base and modifying the error checking accordingly gives the function dec2basevect you were asking for.
The result will be a base-10 vector and you are no longer limited to bases <= 36. The most significant digit will be in index one of this vector. If you need it the other way round, i.e. least significant digit in index one, just do a fliplr to the result.
Due to copyrights by MathWorks, you have to make the necessary modifications to dec2baseon your own.
I found a result that there is a grid of size 9x13 with following properties:
Every cell contains a digit in base 10.
One can read the numbers from the grid by selecting a starting square, go to one of its 8 nearest grid, maintain that direction and concatenate numbers.
For example, if we have the following grid:
340934433
324324893
455423343
Then one can select the leftmost upper number 3 and select direction to the right and down to read numbers 3, 32 and 325.
Now one has to prove that there is a grid of size 9x13 where one can read the squares of 1 to 100, i.e. one can read all of the integers of the form i^2 where i=1,...,100 from the square.
The best grid I found on the net is of size 11x11, given in Solving a recreational square packing problem . But it looks like it is hard to modify the program to find integers in rectangular grid.
So what kind of algorithm would output a suitable grid in a reasonable time?
I just got a key error from this code:
import random, time, sys
N = 9
M = 13
K = 100
# These are the numbers we would like to pack
numbers = [str(i*i) for i in xrange(1, K+1)]
# Build the global list of digits (used for weighted random guess)
digits = "".join(numbers)
def random_digit(n=len(digits)-1):
return digits[random.randint(0, n)]
# By how many lines each of the numbers is currently covered
count = dict((x, 0) for x in numbers)
# Number of actually covered numbers
covered = 0
# All lines in current position (row, cols, diags, counter-diags)
lines = (["*"*N for x in xrange(N)] +
["*"*M for x in xrange(M)] +
["*"*x for x in xrange(1, N)] + ["*"*x for x in xrange(N, 0, -1)] +
["*"*x for x in xrange(1, M)] + ["*"*x for x in xrange(M, 0, -1)])
# lines_of[x, y] -> list of line/char indexes
lines_of = {}
def add_line_of(x, y, L):
try:
lines_of[x, y].append(L)
except KeyError:
lines_of[x, y] = [L]
for y in xrange(N):
for x in xrange(N):
add_line_of(x, y, (y, x))
add_line_of(x, y, (M + x, y))
add_line_of(x, y, (2*M + (x + y), x - max(0, x + y - M + 1)))
add_line_of(x, y, (2*M + 2*N-1 + (x + N-1 - y), x - max(0, x + (M-1 - y) - M + 1)))
# Numbers covered by each line
covered_numbers = [set() for x in xrange(len(lines))]
# Which numbers the string x covers
def cover(x):
c = x + "/" + x[::-1]
return [y for y in numbers if y in c]
# Set a matrix element
def setValue(x, y, d):
global covered
for i, j in lines_of[x, y]:
L = lines[i]
C = covered_numbers[i]
newL = L[:j] + d + L[j+1:]
newC = set(cover(newL))
for lost in C - newC:
count[lost] -= 1
if count[lost] == 0:
covered -= 1
for gained in newC - C:
count[gained] += 1
if count[gained] == 1:
covered += 1
covered_numbers[i] = newC
lines[i] = newL
def do_search(k, r):
start = time.time()
for i in xrange(r):
x = random.randint(0, N-1)
y = random.randint(0, M-1)
setValue(x, y, random_digit())
best = None
attempts = k
while attempts > 0:
attempts -= 1
old = []
for ch in xrange(1):
x = random.randint(0, N-1)
y = random.randint(0, M-1)
old.append((x, y, lines[y][x]))
setValue(x, y, random_digit())
if best is None or covered > best[0]:
now = time.time()
sys.stdout.write(str(covered) + chr(13))
sys.stdout.flush()
attempts = k
if best is None or covered >= best[0]:
best = [covered, lines[:N][:]]
else:
for x, y, o in old[::-1]:
setValue(x, y, o)
print
sys.stdout.flush()
return best
for y in xrange(N):
for x in xrange(N):
setValue(x, y, random_digit())
best = None
while True:
if best is not None:
for y in xrange(M):
for x in xrange(N):
setValue(x, y, best[1][y][x])
x = do_search(100000, M)
if best is None or x[0] > best[0]:
print x[0]
print "\n".join(" ".join(y) for y in x[1])
if best is None or x[0] >= best[0]:
best = x[:]
To create such a grid, I'd start with a list of strings representing the squares of the first K (100) numbers.
Reduce those strings as much as possible, where many are contained within others (for example, 625 contains 25, so 625 covers the squares of 5 and 25).
This should yield an initial list of 81 unique squares, requiring a minimum of about 312 digits:
def construct_optimal_set(K):
# compute a minimal solution:
numbers = [str(n*n) for n in range(0,K+1)]
min_numbers = []
# note: go in reverse direction, biggest to smallest, to maximize elimination of smaller numbers later
while len(numbers) > 0:
i = 0
while i < len(min_numbers):
q = min_numbers[i]
qr = reverse(min_numbers[i])
# check if the first number is contained within any element of min_numbers
if numbers[-1] in q or numbers[-1] in qr:
break
# check if any element of min_numbers is contained within the first number
elif q in numbers[-1] or qr in numbers[-1]:
min_numbers[i] = numbers[-1]
break
i += 1
# if not found, add it
if i >= len(min_numbers):
min_numbers.append(numbers[-1])
numbers = numbers[:-1]
min_numbers.sort()
return min_numbers
This will return a minimal set of squares, with any squares that are subsets of other squares removed. Extend this by concatenating any mostly-overlapping elements (such as 484 and 841 into 4841); I leave that as an exercise, since it will build familiarity with this code.
Then, you assemble these sort of like a cross-word puzzle. As you assemble the values, pack based on probability of possible future overlaps, by computing a weight for each digit (for example, 1's are fairly common, 9's are less common, so given the choice, you would favor overlapping 9's rather than 1's).
Use something like the following code to build a list of all possible values that are represented in the current grid. Use this periodically while building, in order to eliminate squares that are already represented, as well as to test whether your grid is a full solution.
def merge(digits):
result = 0
for i in range(len(digits)-1,-1,-1):
result = result * 10 + digits[i]
return result
def merge_reverse(digits):
result = 0
for i in range(0, len(digits)):
result = result * 10 + digits[i]
return result
# given a grid where each element contains a single numeric digit,
# return list of every ordering of those digits less than SQK,
# such that you pick a starting point and one of eight directions,
# and assemble digits until either end of grid or larger than SQK;
# this will construct only the unique combinations;
# also note that this will not construct a large number of values,
# since for any given direction, there are at most
# (sqrt(n*n + m*m))!
# possible arrangements, and there will rarely be that many.
def construct_lines(grid, k):
# rather than build a dictionary type, use a little more memory to use faster simple array indexes;
# index is #, and value at index indicates existence: 0 = does not exist, >0 means exists in grid
sqk = k*k
combinations = [0]*(sqk+1)
# do all horizontals, since they are easiest
for y in range(len(grid)):
digits = []
for x in range(len(grid[y])):
digits.append(grid[y][x])
# for every possible starting point...
for q in range(1,len(digits)):
number = merge(digits[q:])
if number <= sqk:
combinations[number] += 1
# now do all verticals
# note that if the grid is really square, grid[0] will give an accurate width of all grid[y][] rows
for x in range(len(grid[0])):
digits = []
for y in range(len(grid)):
digits.append(grid[y][x])
# for every possible starting point...
for q in range(1,len(digits)):
number = merge(digits[q:])
if number <= sqk:
combinations[number] += 1
# the longer axis (x or y) in both directions will contain every possible diagonal
# e.g. x is the longer axis here (using random characters to more easily distinguish idea):
# [1 2 3 4]
# [a b c d]
# [. , $ !]
# 'a,' can be obtained by reversing the diagonal starting on the bottom and working up and to the left
# this means that every set must be reversed as well
if len(grid) > len(grid[0]):
# for each y, grab top and bottom in each of two diagonal directions, for a total of four sets,
# and include the reverse of each set
for y in range(len(grid)):
digitsul = [] # origin point upper-left, heading down and right
digitsur = [] # origin point upper-right, heading down and left
digitsll = [] # origin point lower-left, heading up and right
digitslr = [] # origin point lower-right, heading up and left
revx = len(grid[y])-1 # pre-adjust this for computing reverse x coordinate
for deltax in range(len(grid[y])): # this may go off the grid, so check bounds
if y+deltax < len(grid):
digitsul.append(grid[y+deltax][deltax])
digitsll.append(grid[y+deltax][revx - deltax])
for q in range(1,len(digitsul)):
number = merge(digitsul[q:])
if number <= sqk:
combinations[number] += 1
number = merge_reverse(digitsul[q:])
if number <= sqk:
combinations[number] += 1
for q in range(1,len(digitsll)):
number = merge(digitsll[q:])
if number <= sqk:
combinations[number] += 1
number = merge_reverse(digitsll[q:])
if number <= sqk:
combinations[number] += 1
if y-deltax >= 0:
digitsur.append(grid[y-deltax][deltax])
digitslr.append(grid[y-deltax][revx - deltax])
for q in range(1,len(digitsur)):
number = merge(digitsur[q:])
if number <= sqk:
combinations[number] += 1
number = merge_reverse(digitsur[q:])
if number <= sqk:
combinations[number] += 1
for q in range(1,len(digitslr)):
number = merge(digitslr[q:])
if number <= sqk:
combinations[number] += 1
number = merge_reverse(digitslr[q:])
if number <= sqk:
combinations[number] += 1
else:
# for each x, ditto above
for x in range(len(grid[0])):
digitsul = [] # origin point upper-left, heading down and right
digitsur = [] # origin point upper-right, heading down and left
digitsll = [] # origin point lower-left, heading up and right
digitslr = [] # origin point lower-right, heading up and left
revy = len(grid)-1 # pre-adjust this for computing reverse y coordinate
for deltay in range(len(grid)): # this may go off the grid, so check bounds
if x+deltay < len(grid[0]):
digitsul.append(grid[deltay][x+deltay])
digitsll.append(grid[revy - deltay][x+deltay])
for q in range(1,len(digitsul)):
number = merge(digitsul[q:])
if number <= sqk:
combinations[number] += 1
number = merge_reverse(digitsul[q:])
if number <= sqk:
combinations[number] += 1
for q in range(1,len(digitsll)):
number = merge(digitsll[q:])
if number <= sqk:
combinations[number] += 1
number = merge_reverse(digitsll[q:])
if number <= sqk:
combinations[number] += 1
if x-deltay >= 0:
digitsur.append(grid[deltay][x-deltay])
digitslr.append(grid[revy - deltay][x - deltay])
for q in range(1,len(digitsur)):
number = merge(digitsur[q:])
if number <= sqk:
combinations[number] += 1
number = merge_reverse(digitsur[q:])
if number <= sqk:
combinations[number] += 1
for q in range(1,len(digitslr)):
number = merge(digitslr[q:])
if number <= sqk:
combinations[number] += 1
number = merge_reverse(digitslr[q:])
if number <= sqk:
combinations[number] += 1
# now filter for squares only
return [i for i in range(0,k+1) if combinations[i*i] > 0]
Constructing the grid will be computationally expensive overall, but you will only need to run the check function once for each possible placement, to select the best placement.
Optimize placement by finding the subset of overlapping areas where you can place a sequence of numbers - this should be tolerable in terms of time required, because you can cap the number of possible locations to check; e.g. you might cap it at 10 (again, find the optimal number experimentally), such that you test the first 10 possible placements against the function above to determine which placement, if any, adds the most possible squares. As you progress, you will have fewer possible locations in which to insert the numbers, so testing which placement is best becomes computationally less expensive at the same time that your search for possible placements becomes more expensive, balancing out each other.
This will not handle all combinations, and will not pack as tightly as trying every possible arrangement and computing how many squares are covered, so some might be missed, but compared to O((N*M)!), this algorithm will actually complete in your lifetime (I'd actually estimate a few minutes on a decent computer - more if you parallelize the check for placement).