How many ways to represent a number from a given set of numbers - algorithm

I want to know in how many ways can we represent a number x as a sum of numbers from a given set of numbers {a1.a2,a3,...}. Each number can be taken more than once.
For example, if x=4 and a1=1,a2=2, then the ways of representing x=4 are:
1+1+1+1
1+1+2
1+2+1
2+1+1
2+2
Thus the number of ways =5.
I want to know if there exists a formula or some other fast method to do so. I can't brute force through it. I want to write code for it.
Note: x can be as large as 10^18. The number of terms a1,a2,a3,… can be up to 15, and each of a1,a2,a3,… can also be only up to 15.

Calculating the number of combinations can be done in O(log x), disregarding the time it takes to perform matrix multiplication on arbitrarily sized integers.
The number of combinations can be formulated as a recurrence. Let S(n) be the number of ways to make the number n by adding numbers from a set. The recurrence is
S(n) = a_1*S(n-1) + a_2*S(n-2) + ... + a_15*S(n-15),
where a_i is the number of times i occurs in the set. Also, S(n)=0 for n<0. This kind of recurrence can be formulated in terms of a matrix A of size 15*15 (or less is the largest number in the set is smaller). Then, if you have a column vector V containing
S(n-14) S(n-13) ... S(n-1) S(n),
then the result of the matrix multiplication A*V will be
S(n-13) S(n-12) ... S(n) S(n+1).
The A matrix is defined as follows:
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
a_15 a_14 a_13 a_12 a_11 a_10 a_9 a_8 a_7 a_6 a_5 a_4 a_3 a_2 a_1
where a_i is as defined above. The proof that the multiplication of this matrix with a vector of S(n_14) ... S(n) works can be immediately seen by performing the multiplication manually; the last element in the vector will be equal to the right hand side of the recurrence with n+1. Informally, the ones in the matrix shifts the elements in the column vector one row up, and the last row of the matrix calculates the newest term.
In order to calculate an arbitrary term S(n) of the recurrence is to calculate A^n * V, where V is equal to
S(-14) S(-13) ... S(-1) S(0) = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.
In order to get the runtime down to O(log x), one can use exponentiation by squaring to calculate A^n.
In fact, it is sufficient to ignore the column vector altogether, the lower right element of A^n contains the desired value S(n).
In case the above explanation was hard to follow, I have provided a C program that calculates the number of combinations in the way I described above. Beware that it will overflow a 64-bits integer very quickly. You'll be able to get much further with a high-precision floating point type using GMP, though you won't get an exact answer.
Unfortunately, I can't see a fast way to get an exact answer for numbers such at x=10^18, since the answer can be much larger than 10^x.
#include <stdio.h>
typedef unsigned long long ull;
/* highest number in set */
#define N 15
/* perform the matrix multiplication out=a*b */
void matrixmul(ull out[N][N],ull a[N][N],ull b[N][N]) {
ull temp[N][N];
int i,j,k;
for(i=0;i<N;i++) for(j=0;j<N;j++) temp[i][j]=0;
for(k=0;k<N;k++) for(i=0;i<N;i++) for(j=0;j<N;j++)
temp[i][j]+=a[i][k]*b[k][j];
for(i=0;i<N;i++) for(j=0;j<N;j++) out[i][j]=temp[i][j];
}
/* take the in matrix to the pow-th power, return to out */
void matrixpow(ull out[N][N],ull in[N][N],ull pow) {
ull sq[N][N],temp[N][N];
int i,j;
for(i=0;i<N;i++) for(j=0;j<N;j++) temp[i][j]=i==j;
for(i=0;i<N;i++) for(j=0;j<N;j++) sq[i][j]=in[i][j];
while(pow>0) {
if(pow&1) matrixmul(temp,temp,sq);
matrixmul(sq,sq,sq);
pow>>=1;
}
for(i=0;i<N;i++) for(j=0;j<N;j++) out[i][j]=temp[i][j];
}
void solve(ull n,int *a) {
ull m[N][N];
int i,j;
for(i=0;i<N;i++) for(j=0;j<N;j++) m[i][j]=0;
/* create matrix from a[] array above */
for(i=2;i<=N;i++) m[i-2][i-1]=1;
for(i=1;i<=N;i++) m[N-1][N-i]=a[i-1];
matrixpow(m,m,n);
printf("S(%llu): %llu\n",n,m[N-1][N-1]);
}
int main() {
int a[]={1,1,0,0,0,0,0,1,0,0,0,0,0,0,0};
int b[]={1,1,1,1,1,0,0,0,0,0,0,0,0,0,0};
solve(13,a);
solve(80,a);
solve(15,b);
solve(66,b);
return 0;
}

If you want to find all possible ways of representing a number N from a given set of numbers then you should follow a dynamic programming solution as already proposed.
But if you just want to know the number of ways, then you are dealing with the restricted partition function problem.
The restricted partition function p(n, dm) ≡ p(n, {d1, d2, . . . ,
dm}) is a number of partitions of n into positive integers {d1, d2, .
. . , dm}, each not greater than n.
You should also check the wikipedia article on partition function without restrictions where no restrictions apply.
PS. If negative numbers are also allowed then there probably are (countably )infinite ways to represent your sum.
1+1+1+1-1+1
1+1+1+1-1+1-1+1
etc...
PS2. This is more a math question than a programming one

Since order in sum is important it holds:
S( n, {a_1, ..., a_k} ) = sum[ S( n - a_i, {a_1, ..., a_k} ) for i in 1, ..., k ].
That is enough for dynamic programming solution. If values S(i, set) are created from 0 to n, than complexity is O( n*k ).
Edit: Just an idea. Look at one summation as a sequence (s_1, s_2, ..., s_m). Sum of first part of sequence will be larger than n/2 at one point, let it be for index j:
s_1 + s_2 + ... + s_{j-1} < n / 2,
s_1 + s_2 + ... + s_j = S >= n / 2.
There are at most k different sums S, and for each S there are at most k possible last elements s_j. All of possibilities (S,s_j) split sequence sum in 3 parts.
s_1 + s_2 + ... + s_{j-1} = L,
s_j,
s_{j+1} + ... + s_m = R.
It hold n/2 >= L, R > n/2 - max{a_i}. With that, upper formula have more complicated form:
S( n, set ) = sum[ S( n-L-s_j, set )*S( R, set ) for all combinations of (S,s_j) ].
I'm not sure, but I think that with each step it will be needed to 'create' range of
S(x,set) values where range will grow linearly by factor max{a_i}.
Edit 2: #Andrew samples. It is easy to implement first method and it works for 'small' x. Here is python code:
def S( x, ai_s ):
s = [0] * (x+1)
s[0] = 1
for i in xrange(1,x+1):
s[i] = sum( s[i-ai] if i-ai >= 0 else 0 for ai in ai_s )
return s[x]
S( 13, [1,2,8] )
S( 15, [1,2,3,4,5] )
This implementation has problem with memory for large x (>10^5 in python). Since only last max(a_i) values are needed it is possible to implement it with circular buffer.
These values grow very fast, e.g. S(100000, [1,2,8] ) is ~ 10^21503.

Related

Find maximum covered elements without block by obstacles in path

Given MXN matrix where matrix elements are either "." or "*". Where . is representing road and * is representing block or wall. Person can move adjacent forward, down and diagonally, we need to find maximum "." covered by person without blocked by wall. Example(in image)
Can you please suggest me efficient algorithm to approach this problem?
You have to do this: https://en.wikipedia.org/wiki/Flood_fill
Take the biggest flood you can do.
You go through your matrix and find a '.'
Do a flood from that point. The amount of elements you flood the area you always compare it with the maximum you already found. To make this easy you can flood with a letter or a number or whatever you want but not with '.'. What you add instead of '.' consider it as a wall or a '*' so you don't try to flood that area again and again.
Continue to go through the matrix and try to find the next '.'. All the previous '.' where flooded so you won't consider the same area twice.
Redo 2 until you can't find any more '.'. The maximum will contain your answer.
When you have the answer you can go back in the Matrix and you already know the letter or number you flooded the area with the maximum result so you can print the biggest area.
Are you looking for the exact path or only the number of cases?
Edit: here a smallp Python script which creates a random matrix and count the number of cases in each zone defined by your "walls".
import numpy as np
matrix = np.random.randint(2, size=(10, 10))
print(matrix)
M, N = matrix.shape
walked = []
zonesCount = []
def pathCount(x, y):
if x < 0 or y < 0 or x >= M or y >= N:
return 0
if matrix[x, y] == 1: # I replaced * by 1 and . by 0 for easier generation
return 0
if (x, y) in walked:
return 0
walked.append((x, y))
count = 1
for i in [x - 1, x, x + 1]:
for j in [y - 1, y, y + 1]:
if (i, j) != (x, y):
count += pathCount(i, j)
return count
for x in range(M):
for y in range(N):
if not (x, y) in walked:
zonesCount.append(pathCount(x, y))
print('Max zone count :', max(zonesCount))
And here is the result:
[[0 0 1 0 0 0 1 0 1 0]
[1 0 1 0 0 0 1 0 1 1]
[0 1 0 0 1 0 0 1 1 1]
[0 0 1 0 0 0 1 1 0 1]
[1 0 1 1 1 1 0 1 1 0]
[1 0 1 1 1 1 0 1 1 0]
[0 0 0 1 1 1 0 0 0 0]
[1 0 0 1 1 0 0 1 1 0]
[0 1 0 1 0 0 1 0 1 1]
[0 1 1 0 0 0 1 0 1 0]]
Max zone count : 50

Find maximum number of distinct square matrix in a mxn matrix

There is a m x n matrix which contains either 0 or 1. A square submatrix of 2x2 is defined which contains only 0. If such square submatrix is cut from the original matrix then we have to find out the maximum number of such square sub matrices which can be cut from the original matrix. Cutting strictly means no 2 square sub matrix can overlap.
For ex -
This is a 5x5 matrix
0 0 0 1 0
0 0 0 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 0 0
If we cut a square submatrix of 2x2 starting from (0,0) then the remaining matrix is
0 1 0
0 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 0 0
Further 2x2 square sub matrices can be cut
In this give input maximum 3 such matrices can be cut. If I mark them with 'a'
a a 0 1 0
a a a a 0
1 0 a a 0
a a 0 1 0
a a 0 0 0
I have tried the backtracking/recursive approach but it can work only for lower size input. Can anybody suggest a more efficeint approach?
Edit: I have mark matrix elements with "a" to show that this is one sub matrix which can be cut. We have to report only maximum number of 2x2 submatrix (containing all 0) which can be taen from this matrix
Just for the sake of completeness, I changed the script to do some crude recursion, you were right it's difficult to not resort to a recursive way of doing it...
The idea:
f(matrix,count)
IF count > length THEN
length = count
add all options to L
IF L is empty THEN
return
FOR each option in L
FOR each position in option
set position in matrix to 1
f(matrix,count+1)
FOR each position in option
set position in matrix to 0
where options are all 2x2 submatrices with only 0s that are currently in matrix
length = 0
set M to the matrix with 1s and 0s
f(M,0)
In python:
import copy
def possibilities(y):
l = len(y[0]) # horizontal length of matrix
h = len(y) # verticle length of matrix
sub = 2 # length of square submatrix you want to shift in this case 2x2
length = l-sub+1
hieght = h-sub+1
x = [[0,0],[0,1],
[1,0],[1,1]]
# add all 2x2 to list L
L=[]
for i in range(hieght):
for j in range(length):
if y[x[0][0]][x[0][1]]==0 and y[x[1][0]][x[1][1]]==0 and y[x[2][0]][x[2][1]]==0 and y[x[3][0]][x[3][1]]==0:
# create a copy of x
c = copy.deepcopy(x)
L.append(c)
for k in x: # shift submatrix to the right 1
k[1]+=1
(x[0][1],x[1][1],x[2][1],x[3][1]) = (0,1,0,1)
for k in x: # shift submatrix down 1
k[0]+=1
return L
def f(matrix,count):
global length
if count > length:
length = count
L = possibilities(matrix)
if not L:
return
for option in L:
for position in option:
matrix[position[0]][position[1]]=1
f(matrix,count+1)
# reset back to 0
for position in option:
matrix[position[0]][position[1]]=0
length = 0
# matrix
M = [[0,0,1,0,0,0],
[0,0,0,0,0,0],
[1,1,0,0,0,0],
[0,1,1,0,0,0]]
f(M,0)
print(length)

How to create a symmetric matrix of 1's and 0's with constant row and column sum

I'm trying to find an elegant algorithm for creating an N x N matrix of 1's and 0's, under the restrictions:
each row and each column must sum to Q (to be picked freely)
the diagonal must be 0's
the matrix must be symmetrical.
It is not strictly necessary for the matrix to be random (both random and non-random solutions are interesting, however), so for Q even, simply making each row a circular shift of the vector
[0 1 1 0 ... 0 0 0 ... 0 1 1] (for Q=4)
is a valid solution.
However, how to do this for Q odd? Or how to do it for Q even, but in a random fashion?
For those curious, I'm trying to test some phenomena on abstract networks.
I apologize if this has already been answered before, but none of the questions I could find had the symmetric restriction, which seems to make it much more complicated. I don't have a proof that such a matrix always exists, but I do assume so.
The object that you're trying to construct is known more canonically as an undirected d-regular graph (where d = Q). By the handshaking theorem, N and Q cannot both be odd. If Q is even, then connect vertex v to v + k modulo N for k in {-Q/2, -Q/2 + 1, ..., -1, 1, ..., Q/2 - 1, Q/2}. If Q is odd, then N is even. Construct a (Q - 1)-regular graph as before and then add connections from v to v + N/2 modulo N.
If you want randomness, there's a Markov chain whose limiting distribution is uniform on d-regular graphs. You start with any d-regular graph. Repeatedly pick vertices v, w, x, y at random. Whenever the induced subgraph looks like
v----w
x----y ,
flip it to
v w
| |
x y .
You can perhaps always follow your circular shift algorithm, when possible.
The only condition you need to follow while using the circular shift algorithm is to maintain the symmetric nature in the first row.
i.e. keeping Q 1's in the first row so that Q[0,1] to Q[0,N-1] {Assuming 0 indexed rows and cols, Q[0,0] is 0.} is symmetric, a simple example being 110010011.
Hence, N = 10, Q = 5, you can get many possible arrangements such as:
0 1 0 0 1 1 1 0 0 1
1 0 1 0 0 1 1 1 0 0
0 1 0 1 0 0 1 1 1 0
0 0 1 0 1 0 0 1 1 1
1 0 0 1 0 1 0 0 1 1
1 1 0 0 1 0 1 0 0 1
1 1 1 0 0 1 0 1 0 0
0 1 1 1 0 0 1 0 1 0
0 0 1 1 1 0 0 1 0 1
1 0 0 1 1 1 0 0 1 0
or
0 1 1 0 0 1 0 0 1 1
1 0 1 1 0 0 1 0 0 1
1 1 0 1 1 0 0 1 0 0
0 1 1 0 1 1 0 0 1 0
0 0 1 1 0 1 1 0 0 1
1 0 0 1 1 0 1 1 0 0
0 1 0 0 1 1 0 1 1 0
0 0 1 0 0 1 1 0 1 1
1 0 0 1 0 0 1 1 0 1
1 1 0 0 1 0 0 1 1 0
But as you can see for odd N(that means even N-1) and odd Q there can't be any such symmetric distribution.. Hope it helped.

Order a set of numbers to maximize adjacent differences

Given a set of N numbers x1, x2, ..., xN, how can you find an ordering of them to maximize the minimum absolute difference between adjacent numbers? This is probably an NP hard problem, so any efficient approximate method will do.
Let's say you've defined your data as x_i for i=1, ..., n. We can define binary variables p_{ij} for i=1, ..., n, and j=1, ..., n, which are 1 if number i is in sorted order j and 0 otherwise. Adding a variable e, our optimization model would be something like:
The constraints with the absolute values ensure that e (our minimum gap) does not exceed the gap between each pair of adjacent elements in our sorted sequence. However, absolute values aren't allowed in linear optimization models, and in general you need to add a binary variable to model an absolute value being greater than or equal to some other value. So let's add binary variable r_j, j=2, ..., n, and replace our problematic constraints:
Here M is a large number; 2(max(x) - min(x)) should be sufficiently large. Now, we're ready to actually implement this model. You can use any MIP solver; I'll use the lpSolveAPI in R because it's free and easily accessible. p_{ij} are stored in variables 1 through n^2; r_j are stored in variables n^2+1 through n^2+n-1; and e is stored in variable n^2+n.
x = 1:5
n = length(x)
M = 2*(max(x) - min(x))
library(lpSolveAPI)
mod = make.lp(0, n^2+n)
set.type(mod, 1:(n^2+n-1), "binary")
set.objfn(mod, c(rep(0, n^2+n-1), 1))
lp.control(mod, sense="max")
for (j in 2:n) {
base.cons <- rep(0, n^2+n)
base.cons[seq(j-1, by=n, length.out=n)] = x
base.cons[seq(j, by=n, length.out=n)] = -x
base.cons[n^2+j-1] = M
first.cons = base.cons
first.cons[n^2+n] = -1
add.constraint(mod, first.cons, ">=", 0)
second.cons = -base.cons
second.cons[n^2+n] = -1
add.constraint(mod, second.cons, ">=", -M)
}
for (j in 1:n) {
this.cons = rep(0, n^2+n)
this.cons[seq(j, by=n, length.out=n)] = 1
add.constraint(mod, this.cons, "=", 1)
}
for (i in 1:n) {
this.cons = rep(0, n^2+n)
this.cons[seq((i-1)*n+1, i*n)] = 1
add.constraint(mod, this.cons, "=", 1)
}
Now we're ready to solve the model:
solve(mod)
# [1] 0
get.objective(mod)
# [1] 2
get.variables(mod)
# [1] 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 1 2
And lastly we can extract the sorted list using the x_i and p_{ij} variables:
sapply(1:n, function(j) sum(get.variables(mod)[seq(j, by=n, length.out=n)]*x))
# [1] 1 3 5 2 4

converting sequences to lex order

I have a function that generates binary sequences with a fixed number of 1's (the rest are 0's). I need a function that takes a sequences and returns the position of that sequence in lexicographic order. For example, the 10 sequences of length 5 with 3 1's are
0 0 1 1 1
0 1 0 1 1
0 1 1 0 1
0 1 1 1 0
1 0 0 1 1
1 0 1 0 1
1 0 1 1 0
1 1 0 0 1
1 1 0 1 0
1 1 1 0 0
I need a function that takes, for example 0 1 1 0 1 and returns 3 since it's the third in the list.
The only thing I can think of, which is way too inefficient, is to generate all of the sequences (easy), store them (takes too much space), then search for the given sequence in the list (too slow), and return its position. Is there a faster way to do this? Some easy trick that I don't see?
We call the set of sequences of length n with k 1's binseq(n,k). This problem can then be solved recursively, as follows:
Base case: If S has length 1, it's in position 1.
If S starts with a 0, its position is the same as the position of tail(S) (S with the first element removed) in binseq(n-1, k).
If S starts with a 1, its position is equal to the position of tail(S) in binseq(n-1, k-1) plus the number of sequences in binseq(n-1, k).
In python code:
#!/usr/bin/env python
def binom(n, k):
result = 1
for i in range(1, k+1):
result = result * (n-i+1) / i
return result
def lexpos(seq):
if len(seq) == 1:
return 1
elif seq[0] == 0:
return lexpos(seq[1:])
else:
return binom(len(seq)-1, seq.count(1)) + lexpos(seq[1:])
Or the iterative version, as suggested by Abhishek Bansal:
def lexpos_iter(seq):
pos = 1
for i in xrange(len(seq)):
if seq[i] == 1:
pos += binom(len(seq)-i-1, seq[i:].count(1))
return pos

Resources