Related
There is already a topic about this task, but I'd like to ask about my specific approach.
The task is:
Let A be a non-empty array consisting of N integers.
The abs sum of two for a pair of indices (P, Q) is the absolute value
|A[P] + A[Q]|, for 0 ≤ P ≤ Q < N.
For example, the following array A:
A[0] = 1 A1 = 4 A[2] = -3 has pairs of indices (0, 0), (0,
1), (0, 2), (1, 1), (1, 2), (2, 2). The abs sum of two for the pair
(0, 0) is A[0] + A[0] = |1 + 1| = 2. The abs sum of two for the pair
(0, 1) is A[0] + A1 = |1 + 4| = 5. The abs sum of two for the pair
(0, 2) is A[0] + A[2] = |1 + (−3)| = 2. The abs sum of two for the
pair (1, 1) is A1 + A1 = |4 + 4| = 8. The abs sum of two for the
pair (1, 2) is A1 + A[2] = |4 + (−3)| = 1. The abs sum of two for
the pair (2, 2) is A[2] + A[2] = |(−3) + (−3)| = 6. Write a function:
def solution(A)
that, given a non-empty array A consisting of N integers, returns the
minimal abs sum of two for any pair of indices in this array.
For example, given the following array A:
A[0] = 1 A1 = 4 A[2] = -3 the function should return 1, as
explained above.
Given array A:
A[0] = -8 A1 = 4 A[2] = 5 A[3] =-10 A[4] = 3 the
function should return |(−8) + 5| = 3.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000]; each element of array A
is an integer within the range [−1,000,000,000..1,000,000,000].
The official solution is O(N*M^2), but I think it could be solved in O(N).
My approach is to first get rid of duplicates and sort the array. Then we check both ends and sompare the abs sum moving the ends by one towards each other. We try to move the left end, the right one or both. If this doesn't improve the result, our sum is the lowest. My code is:
def solution(A):
A = list(set(A))
n = len(A)
A.sort()
beg = 0
end = n - 1
min_sum = abs(A[beg] + A[end])
while True:
min_left = abs(A[beg+1] + A[end]) if beg+1 < n else float('inf')
min_right = abs(A[beg] + A[end-1]) if end-1 >= 0 else float('inf')
min_both = abs(A[beg+1] + A[end-1]) if beg+1 < n and end-1 >= 0 else float('inf')
min_all = min([min_left, min_right, min_both])
if min_sum <= min_all:
return min_sum
if min_left == min_all:
beg += 1
min_sum = min_left
elif min_right == min_all:
end -= 1
min_sum = min_right
else:
beg += 1
end -= 1
min_sum = min_both
It passes almost all of the tests, but not all. Is there some bug in my code or the approach is wrong?
EDIT:
After the aka.nice answer I was able to fix the code. It scores 100% now.
def solution(A):
A = list(set(A))
n = len(A)
A.sort()
beg = 0
end = n - 1
min_sum = abs(A[beg] + A[end])
while beg <= end:
min_left = abs(A[beg+1] + A[end]) if beg+1 < n else float('inf')
min_right = abs(A[beg] + A[end-1]) if end-1 >= 0 else float('inf')
min_all = min(min_left, min_right)
if min_all < min_sum:
min_sum = min_all
if min_left <= min_all:
beg += 1
else:
end -= 1
return min_sum
Just take this example for array A
-11 -5 -2 5 6 8 12
and execute your algorithm step by step, you get a premature return:
beg=0
end=6
min_sum=1
min_left=7
min_right=3
min_both=3
min_all=3
return min_sum
though there is a better solution abs(5-5)=0.
Hint: you should check the sign of A[beg] and A[end] to decide whether to continue or exit the loop. What to do if both >= 0, if both <= 0, else ?
Note that A.sort() has a non neglectable cost, likely O(N*log(N)), it will dominate the cost of the solution you exhibit.
By the way, what is M in the official cost O(N*M^2)?
And the link you provide is another problem (sum all the elements of A or their opposite).
Given the first 9 natural numbers we have to assign + or - sign to each number such that the sum of the resulting sequence is equal to the required number.
For example, we are given the first 9 natural numbers {1,2,3,4,5,6,7,8,9} and have to get the value 1.
The output can be {1,-2,3,4,-5,-6,7,8,-9} or any one of the other possibilities.
if the value is -5, then the output can be {-1,-2,-3,4,-5,-6,7,-8,9}.
We know that the sum will always be odd.
I am unable to come up with an algorithm that could solve this problem.
My idea was to use a greedy approach.(we can assume the sequence is sorted)
Start from the largest value (right side of the sequence).
Sum the numbers till current index.
Check if the sum of sequence is lower than absolute value of the target number.
If yes, then assign the + to the number at the current index otherwise a -.
Repeat till we reach the first number.
Based on my algorithm the output for,
1 is {1 -2 3 4 -5 6 -7 8 -9}
15 is {1 2 -3 4 -5 6 7 -8 9}. Giving me the sum of the sequence as 13 (completely wrong).
Here is my code,
public static int[] sequenceOfSigns(int[] num, int sum) {
int n = num[num.length - 1];
int upperBound = (n * (n + 1)) / 2;
if (sum % 2 == 0 || sum < -upperBound || sum > upperBound)
return null;
int tempSum = 0;
for (int i = num.length - 1; i >= 0; i--) {
tempSum += num[i];
boolean flag;
if (i != 0)
flag = tempSum >= Math.abs(sum);
else
flag = tempSum > Math.abs(sum);
if (flag) {
num[i] = -num[i];
tempSum += num[i] * 2;
}
}
return num;
}
Any ideas on how to modify the algorithm would be greatly appreciated.
Additionally can we extend the problem to n natural numbers? or is it an NP-problem?
EDIT
I have solved this problem another way.
Here is the solution with proof that was inspired by my brother's suggestion on how to solve the problem. His suggestion is superbly elegant.
My Proof
Given a sequence of natural numbers {1,2,3,4,5,6,7,8,9} and an odd target sum of x; we are to modify the sign(+/-) of the numbers in the sequence such that the sum of the elements equals the target number.
Let A = {1,2,3,4,5,6,7,8,9}
x is the target number (odd).
Let us consider a set S which contains all the elements of A that should be made negative.
Then we have the relation,
Sum of all elements in A - (2 * Sum of all elements in S) = |x|
[This is the key idea suggested by my brother]
Rearranging the equation we get,
Sum of all elements in S = (Sum of all elements in A - |x|)/2 .... (1)
Now, we select the largest value in A which is lesser than or equal to the sum in (1).
We subtract this from the sum in (1).
We perform this operation, iteratively, till the sum in (1) becomes 0.
A point to note is that each value from A can only be selected once.
This is our greedy algorithm.
This choice of largest element in A which is lesser than or equal to the sum at each step is a safe greedy choice. Since, the selected values will form the set of numbers which should be negative the sum of the selected values and the sum in (1) should be same.
Now, if we select a value greater than the sum in (1) the difference would be a negative value and that is a contradiction. Hence, we cannot select a greater value.
If we arbitrarily choose any value that is lesser than or equal to the sum in (1), the difference is positive or 0. We can easily replace the selected value with the largest value that satisfies the conditions and the constraints are still obeyed. So, our choice is a safe choice.
The values selected in the previous step form the set S.
and our required sequence is the ordered set formed from the set,
(A - S) U -(elements in S)
If x is negative we simply flip the signs in the ordered set obtained above. Since, the solutions for x and -x are symmetric.
This concludes our proof and solution for the greedy algorithm
CODE
public static int[] sequenceOfSigns(int[] num, int sum) {
int n = num[num.length - 1];
int upperBound = (n * (n + 1)) / 2;
if (sum % 2 == 0 || sum < -upperBound || sum > upperBound)
return null;
int negativeSeqSum = (upperBound - Math.abs(sum)) / 2;
for (int i = num.length - 1; i >= 0; i--) {
if (num[i] <= negativeSeqSum) {
negativeSeqSum -= num[i];
num[i] = -num[i];
}
if (negativeSeqSum == 0)
break;
}
if (sum == -Math.abs(sum))
for (int i = 0; i < num.length; i++)
num[i] = -num[i];
return num;
}
You could solve this by using the following intuition:
Any number could get a + or - sign.
Lets say the last number is 'n' and you could mark it positive or negative
If you mark it positive:
Then remaining sum is S - n and you have to solve the problem for n-1 numbers and target sum = S - n
If you mark it negative:
Then remaining sum is S + n and you have to solve the problem for n-1 numbers and target sum = S + n
Therefore you could write the following recursion:
isPossible(N, S) = isPossible(N-1, S-N) || isPossible(N-1, S+N)
You could memoize the solution at each combination of (N, S) and store the result in a map of
Pair ( N, S) => Boolean
Now from the map you could go through the pairs which have true values i.e. ( for the pair's 'N' numbers it is possible to get sum equal to the pair's S) and get the appropriate signs like below example :
The map for the case N = 9 and S = -5 looks like:
{(1, -31)=false, (2, -33)=false, (6, -29)=false, (2, -37)=false, (5, -35)=false, (4, -4)=true, (2, -41)=false, (3, -10)=false, (3, -14)=false, (4, -16)=false, (3, -18)=false, (3, -22)=false, (3, -26)=false, (7, -22)=true, (4, -28)=false, (3, -30)=false, (1, -1)=true, (3, -34)=false, (2, -3)=true, (1, -5)=false, (2, -7)=false, (1, -9)=false, (4, -40)=false, (1, -49)=false, (2, -11)=false, (1, -13)=false, (5, -9)=true, (9, -5)=true, (1, -45)=false, (2, -15)=false, (1, -17)=false, (1, -41)=false, (2, -19)=false, (6, -15)=true, (1, -21)=false, (1, -37)=false, (2, -23)=false, (1, -25)=false, (5, -21)=false, (1, -33)=false, (2, -27)=false, (1, -29)=false, (2, -31)=false, (3, 0)=true, (2, -35)=false, (2, -39)=false, (3, -8)=false, (3, -12)=false, (2, -47)=false, (4, -14)=false, (4, -18)=false, (8, -14)=true, (3, -20)=false, (3, -24)=false, (4, -26)=false, (4, -30)=false, (3, -32)=false, (1, -3)=false, (3, -36)=false, (2, -5)=false, (1, -7)=false, (2, -9)=false, (1, -11)=false, (3, -44)=false, (2, -13)=false, (1, -15)=false, (1, -43)=false, (2, -17)=false, (1, -19)=false, (1, -39)=false, (2, -21)=false, (1, -23)=false, (1, -35)=false, (2, -25)=false, (1, -27)=false, (5, -23)=false, (2, -29)=false}
Start with the key = Pair(9, -5) is it there in the map with a value of "true"?
Yes it is there, now look for two possibilities : Pair(8, -5-9) or Pair(8, -5+9)
It turns out that Pair(8, -14) exists, so you need to add +9 to the list because you need to look for S - n i.e. you are subtracting n from the remaining S you are looking for. Then update S = S- n = -5 - 9 = -14.
Now look for two possiblities : Pair(7, -14-8) or Pair(7, -14+8). It turns out that Pair(7, -22) exists, so you need to add +8 to the list because you need to look for S - n which means you subtracted n = 8 from the remaining S you are looking for. Then update S = S - n = -14 - 8 = -22
....
....
The final result will be [-1, -2, 3, -4, -5, -6, -7, 8, 9]
Code is - ( you can play with the code here by modifying N, S)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Main {
private static Map<Pair, Boolean> map = new HashMap<>();
public static void main(String... args) {
int N = 9;
int S = -5;
Integer[] result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
S = 15;
result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
S = 45;
result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
S = -45;
result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
S = 4;
result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
}
public static Integer[] sequenceOfSigns(int N, int sum) {
boolean isPossible = helper(N, sum);
if ( isPossible ) {
int s = sum;
List<Integer> res_list = new ArrayList<>();
for ( int n = N; n >= 1; n-- ) {
Pair p = new Pair(n, s);
if ( n == 1 ) {
res_list.add(s);
} else if ( map.containsKey(p) && map.get(p) ) {
Pair p1 = new Pair(n-1, s+n);
Pair p2 = new Pair(n-1, s-n);
if (map.containsKey(p1)) {
res_list.add(-n);
s = s + n;
} else if ( map.containsKey(p2)) {
res_list.add(n);
s = s - n;
}
}
}
Collections.reverse(res_list);
if ( !res_list.isEmpty() ) return res_list.toArray(new Integer[] {});
}
return null;
}
private static boolean helper(int N, int S) {
if ( N == 0 ) return S == 0;
Pair p = new Pair(N, S);
if ( map.containsKey(p) ) {
return map.get(p);
}
boolean val = helper(N-1, S - N) || helper(N-1, S + N);
map.put(p, val);
return val;
}
static class Pair {
int N, S;
public Pair(int N, int S) {
this.N = N;
this.S = S;
}
#Override
public boolean equals(Object obj) {
if ( obj instanceof Pair ) {
Pair other = (Pair)obj;
return other.N == N && other.S == S;
}
return false;
}
#Override
public int hashCode() {
return 31*31*N + 31*S;
}
#Override
public String toString() {
return "(" + N + ", " + S + ")";
}
}
}
Output:
Input = 9, -5 : [-1, -2, 3, -4, -5, -6, -7, 8, 9]
Input = 9, 15 : [-1, -2, -3, -4, -5, 6, 7, 8, 9]
Input = 9, 45 : [1, 2, 3, 4, 5, 6, 7, 8, 9]
Input = 9, -45 : [-1, -2, -3, -4, -5, -6, -7, -8, -9]
Input = 9, 4 : Not possible
There are 2^9 = 512 possibilities. Why not brute force?
var hash = {0: [[]]};
for (let i=1; i<=9; i++){
let new_hash = {};
for (let _sum in hash){
let sum = Number(_sum);
let new_sum = sum + i;
if (new_sum in new_hash)
new_hash[new_sum] = new_hash[new_sum].concat(hash[sum].map(x => x.concat(i)));
else
new_hash[new_sum] = hash[sum].map(x => x.concat(i));
new_sum = sum - i;
if (new_sum in new_hash)
new_hash[new_sum] = new_hash[new_sum].concat(hash[sum].map(x => x.concat(-i)));
else
new_hash[new_sum] = hash[sum].map(x => x.concat(-i));
}
hash = new_hash;
}
console.log(JSON.stringify(hash[1]));
console.log('');
console.log(JSON.stringify(hash[15]));
This is a question given in this presentation. Dynamic Programming
now i have implemented the algorithm using recursion and it works fine for small values. But when n is greater than 30 it becomes really slow.The presentation mentions that for large values of n one should consider something similar to
the matrix form of Fibonacci numbers .I am having trouble undestanding how to use the matrix form of Fibonacci numbers to come up with a solution.Can some one give me some hints or pseudocode
Thanks
Yes, you can use the technique from fast Fibonacci implementations to solve this problem in time O(log n)! Here's how to do it.
Let's go with your definition from the problem statement that 1 + 3 is counted the same as 3 + 1. Then you have the following recurrence relation:
A(0) = 1
A(1) = 1
A(2) = 1
A(3) = 2
A(k+4) = A(k) + A(k+1) + A(k+3)
The matrix trick here is to notice that
| 1 0 1 1 | |A( k )| |A(k) + A(k-2) + A(k-3)| |A(k+1)|
| 1 0 0 0 | |A(k-1)| | A( k ) | |A( k )|
| 0 1 0 0 | |A(k-2)| = | A(k-1) | = |A(k-1)|
| 0 0 1 0 | |A(k-3)| | A(k-2) | = |A(k-2)|
In other words, multiplying a vector of the last four values in the series produces a vector with those values shifted forward by one step.
Let's call that matrix there M. Then notice that
|A( k )| |A(k+2)|
|A(k-1)| |A(k+1)|
M^2 |A(k-2)| = |A( k )|
|A(k-3)| |A(k-1)|
In other words, multiplying by the square of this matrix shifts the series down two steps. More generally:
|A( k )| | A(k+n) |
|A(k-1)| |A(k-1 + n)|
M^n |A(k-2)| = |A(k-2 + n)|
|A(k-3)| |A(k-3 + n)|
So multiplying by Mn shifts the series down n steps. Now, if we want to know the value of A(n+3), we can just compute
|A(3)| |A(n+3)|
|A(2)| |A(n+2)|
M^n |A(1)| = |A(n+1)|
|A(0)| |A(n+2)|
and read off the top entry of the vector! This can be done in time O(log n) by using exponentiation by squaring. Here's some code that does just that. This uses a matrix library I cobbled together a while back:
#include "Matrix.hh"
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
/* Naive implementations of A. */
uint64_t naiveA(int n) {
if (n == 0) return 1;
if (n == 1) return 1;
if (n == 2) return 1;
if (n == 3) return 2;
return naiveA(n-1) + naiveA(n-3) + naiveA(n-4);
}
/* Constructs and returns the giant matrix. */
Matrix<4, 4, uint64_t> M() {
Matrix<4, 4, uint64_t> result;
fill(result.begin(), result.end(), uint64_t(0));
result[0][0] = 1;
result[0][2] = 1;
result[0][3] = 1;
result[1][0] = 1;
result[2][1] = 1;
result[3][2] = 1;
return result;
}
/* Constructs the initial vector that we multiply the matrix by. */
Vector<4, uint64_t> initVec() {
Vector<4, uint64_t> result;
result[0] = 2;
result[1] = 1;
result[2] = 1;
result[3] = 1;
return result;
}
/* O(log n) time for raising a matrix to a power. */
Matrix<4, 4, uint64_t> fastPower(const Matrix<4, 4, uint64_t>& m, int n) {
if (n == 0) return Identity<4, uint64_t>();
auto half = fastPower(m, n / 2);
if (n % 2 == 0) return half * half;
else return half * half * m;
}
/* Fast implementation of A(n) using matrix exponentiation. */
uint64_t fastA(int n) {
if (n == 0) return 1;
if (n == 1) return 1;
if (n == 2) return 1;
if (n == 3) return 2;
auto result = fastPower(M(), n - 3) * initVec();
return result[0];
}
/* Some simple test code showing this in action! */
int main() {
for (int i = 0; i < 25; i++) {
cout << setw(2) << i << ": " << naiveA(i) << ", " << fastA(i) << endl;
}
}
Now, how would this change if 3 + 1 and 1 + 3 were treated as equivalent? This means that we can think about solving this problem in the following way:
Let A(n) be the number of ways to write n as a sum of 1s, 3s, and 4s.
Let B(n) be the number of ways to write n as a sum of 1s and 3s.
Let C(n) be the number of ways to write n as a sum of 1s.
We then have the following:
A(n) = B(n) for all n ≤ 3, since for numbers in that range the only options are to use 1s and 3s.
A(n + 4) = A(n) + B(n + 4), since your options are either (1) use a 4 or (2) not use a 4, leaving the remaining sum to use 1s and 3s.
B(n) = C(n) for all n ≤ 2, since for numbers in that range the only options are to use 1s.
B(n + 3) = B(n) + C(n + 3), sine your options are either (1) use a 3 or (2) not use a 3, leaving the remaining sum to use only 1s.
C(0) = 1, since there's only one way to write 0 as a sum of no numbers.
C(n+1) = C(n), since the only way to write something with 1s is to pull out a 1 and write the remaining number as a sum of 1s.
That's a lot to take in, but do notice the following: we ultimately care about A(n), and to evaluate it, we only need to know the values of A(n), A(n-1), A(n-2), A(n-3), B(n), B(n-1), B(n-2), B(n-3), C(n), C(n-1), C(n-2), and C(n-3).
Let's imagine, for example, that we know these twelve values for some fixed value of n. We can learn those twelve values for the next value of n as follows:
C(n+1) = C(n)
B(n+1) = B(n-2) + C(n+1) = B(n-2) + C(n)
A(n+1) = A(n-3) + B(n+1) = A(n-3) + B(n-2) + C(n)
And the remaining values then shift down.
We can formulate this as a giant matrix equation:
A( n ) A(n-1) A(n-2) A(n-3) B( n ) B(n-1) B(n-2) C( n )
| 0 0 0 1 0 0 1 1 | |A( n )| = |A(n+1)|
| 1 0 0 0 0 0 0 0 | |A(n-1)| = |A( n )|
| 0 1 0 0 0 0 0 0 | |A(n-2)| = |A(n-1)|
| 0 0 1 0 0 0 0 0 | |A(n-3)| = |A(n-2)|
| 0 0 0 0 0 0 1 1 | |B( n )| = |B(n+1)|
| 0 0 0 0 1 0 0 0 | |B(n-1)| = |B( n )|
| 0 0 0 0 0 1 0 0 | |B(n-2)| = |B(n-1)|
| 0 0 0 0 0 0 0 1 | |C( n )| = |C(n+1)|
Let's call this gigantic matrix here M. Then if we compute
|2| // A(3) = 2, since 3 = 3 or 3 = 1 + 1 + 1
|1| // A(2) = 1, since 2 = 1 + 1
|1| // A(1) = 1, since 1 = 1
M^n |1| // A(0) = 1, since 0 = (empty sum)
|2| // B(3) = 2, since 3 = 3 or 3 = 1 + 1 + 1
|1| // B(2) = 1, since 2 = 1 + 1
|1| // B(1) = 1, since 1 = 1
|1| // C(3) = 1, since 3 = 1 + 1 + 1
We'll get back a vector whose first entry is A(n+3), the number of ways to write n+3 as a sum of 1's, 3's, and 4's. (I've actually coded this up to check it - it works!) You can then use the technique for computing Fibonacci numbers using a matrix to a power efficiently that you saw with Fibonacci numbers to solve this in time O(log n).
Here's some code doing that:
#include "Matrix.hh"
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
/* Naive implementations of A, B, and C. */
uint64_t naiveC(int n) {
return 1;
}
uint64_t naiveB(int n) {
return (n < 3? 0 : naiveB(n-3)) + naiveC(n);
}
uint64_t naiveA(int n) {
return (n < 4? 0 : naiveA(n-4)) + naiveB(n);
}
/* Constructs and returns the giant matrix. */
Matrix<8, 8, uint64_t> M() {
Matrix<8, 8, uint64_t> result;
fill(result.begin(), result.end(), uint64_t(0));
result[0][3] = 1;
result[0][6] = 1;
result[0][7] = 1;
result[1][0] = 1;
result[2][1] = 1;
result[3][2] = 1;
result[4][6] = 1;
result[4][7] = 1;
result[5][4] = 1;
result[6][5] = 1;
result[7][7] = 1;
return result;
}
/* Constructs the initial vector that we multiply the matrix by. */
Vector<8, uint64_t> initVec() {
Vector<8, uint64_t> result;
result[0] = 2;
result[1] = 1;
result[2] = 1;
result[3] = 1;
result[4] = 2;
result[5] = 1;
result[6] = 1;
result[7] = 1;
return result;
}
/* O(log n) time for raising a matrix to a power. */
Matrix<8, 8, uint64_t> fastPower(const Matrix<8, 8, uint64_t>& m, int n) {
if (n == 0) return Identity<8, uint64_t>();
auto half = fastPower(m, n / 2);
if (n % 2 == 0) return half * half;
else return half * half * m;
}
/* Fast implementation of A(n) using matrix exponentiation. */
uint64_t fastA(int n) {
if (n == 0) return 1;
if (n == 1) return 1;
if (n == 2) return 1;
if (n == 3) return 2;
auto result = fastPower(M(), n - 3) * initVec();
return result[0];
}
/* Some simple test code showing this in action! */
int main() {
for (int i = 0; i < 25; i++) {
cout << setw(2) << i << ": " << naiveA(i) << ", " << fastA(i) << endl;
}
}
This is a very interesting sequence. It is almost but not quite the order-4 Fibonacci (a.k.a. Tetranacci) numbers. Having extracted the doubling formulas for Tetranacci from its companion matrix, I could not resist doing it again for this very similar recurrence relation.
Before we get into the actual code, some definitions and a short derivation of the formulas used are in order. Define an integer sequence A such that:
A(n) := A(n-1) + A(n-3) + A(n-4)
with initial values A(0), A(1), A(2), A(3) := 1, 1, 1, 2.
For n >= 0, this is the number of integer compositions of n into parts from the set {1, 3, 4}. This is the sequence that we ultimately wish to compute.
For convenience, define a sequence T such that:
T(n) := T(n-1) + T(n-3) + T(n-4)
with initial values T(0), T(1), T(2), T(3) := 0, 0, 0, 1.
Note that A(n) and T(n) are simply shifts of each other. More precisely, A(n) = T(n+3) for all integers n. Accordingly, as elaborated by another answer, the companion matrix for both sequences is:
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
[1 1 0 1]
Call this matrix C, and let:
a, b, c, d := T(n), T(n+1), T(n+2), T(n+3)
a', b', c', d' := T(2n), T(2n+1), T(2n+2), T(2n+3)
By induction, it can easily be shown that:
[0 1 0 0]^n = [d-c-a c-b b-a a]
[0 0 1 0] [ a d-c c-b b]
[0 0 0 1] [ b b+a d-c c]
[1 1 0 1] [ c c+b b+a d]
As seen above, for any n, C^n can be fully determined from its rightmost column alone. Furthermore, multiplying C^n with its rightmost column produces the rightmost column of C^(2n):
[d-c-a c-b b-a a][a] = [a'] = [a(2d - 2c - a) + b(2c - b)]
[ a d-c c-b b][b] [b'] [ a^2 + c^2 + 2b(d - c)]
[ b b+a d-c c][c] [c'] [ b(2a + b) + c(2d - c)]
[ c c+b b+a d][d] [d'] [ b^2 + d^2 + 2c(a + b)]
Thus, if we wish to compute C^n for some n by repeated squaring, we need only perform matrix-vector multiplication per step instead of the full matrix-matrix multiplication.
Now, the implementation, in Python:
# O(n) integer additions or subtractions
def A_linearly(n):
a, b, c, d = 0, 0, 0, 1 # T(0), T(1), T(2), T(3)
if n >= 0:
for _ in range(+n):
a, b, c, d = b, c, d, a + b + d
else: # n < 0
for _ in range(-n):
a, b, c, d = d - c - a, a, b, c
return d # because A(n) = T(n+3)
# O(log n) integer multiplications, additions, subtractions.
def A_by_doubling(n):
n += 3 # because A(n) = T(n+3)
if n >= 0:
a, b, c, d = 0, 0, 0, 1 # T(0), T(1), T(2), T(3)
else: # n < 0
a, b, c, d = 1, 0, 0, 0 # T(-1), T(0), T(1), T(2)
# Unroll the final iteration to avoid computing extraneous values
for i in reversed(range(1, abs(n).bit_length())):
w = a*(2*(d - c) - a) + b*(2*c - b)
x = a*a + c*c + 2*b*(d - c)
y = b*(2*a + b) + c*(2*d - c)
z = b*b + d*d + 2*c*(a + b)
if (n >> i) & 1 == 0:
a, b, c, d = w, x, y, z
else: # (n >> i) & 1 == 1
a, b, c, d = x, y, z, w + x + z
if n & 1 == 0:
return a*(2*(d - c) - a) + b*(2*c - b) # w
else: # n & 1 == 1
return a*a + c*c + 2*b*(d - c) # x
print(all(A_linearly(n) == A_by_doubling(n) for n in range(-1000, 1001)))
Because it was rather trivial to code, the sequence is extended to negative n in the usual way. Also provided is a simple linear implementation to serve as a point of reference.
For n large enough, the logarithmic implementation above is 10-20x faster than directly exponentiating the companion matrix with numpy, by a simple (i.e. not rigorous, and likely flawed) timing comparison. And by my estimate, it would still take ~100 years to compute A(10**12)! Even though the algorithm above has room for improvement, that number is simply too large. On the other hand, computing A(10**12) mod M for some M is much more attainable.
A direct relation to Lucas and Fibonacci numbers
It turns out that T(n) is even closer to the Fibonacci and Lucas numbers than it is to Tetranacci. To see this, note that the characteristic polynomial for T(n) is x^4 - x^3 - x - 1 = 0 which factors into (x^2 - x - 1)(x^2 + 1) = 0. The first factor is the characteristic polynomial for Fibonacci & Lucas! The 4 roots of (x^2 - x - 1)(x^2 + 1) = 0 are the two Fibonacci roots, phi and psi = 1 - phi, and i and -i--the two square roots of -1.
The closed-form expression or "Binet" formula for T(n) will have the general form:
T(n) = U(n) + V(n)
U(n) = p*(phi^n) + q*(psi^n)
V(n) = r*(i^n) + s*(-i)^n
for some constant coefficients p, q, r, s.
Using the initial values for T(n), solving for the coefficients, applying some algebra, and noting that the Lucas numbers have the closed-form expression: L(n) = phi^n + psi^n, we can derive the following relations:
L(n+1) - L(n) L(n-1) F(n) + F(n-2)
U(n) = ------------- = -------- = ------------
5 5 5
where L(n) is the n'th Lucas number with L(0), L(1) := 2, 1 and F(n) is the n'th Fibonacci number with F(0), F(1) := 0, 1. And we also have:
V(n) = 1 / 5 if n = 0 (mod 4)
| -2 / 5 if n = 1 (mod 4)
| -1 / 5 if n = 2 (mod 4)
| 2 / 5 if n = 3 (mod 4)
Which is ugly, but trivial to code. Note that the numerator of V(n) can also be succinctly expressed as cos(n*pi/2) - 2sin(n*pi/2) or (3-(-1)^n) / 2 * (-1)^(n(n+1)/2), but we use the piece-wise definition for clarity.
Here's an even nicer, more direct identity:
T(n) + T(n+2) = F(n)
Essentially, we can compute T(n) (and therefore A(n)) by using Fibonacci & Lucas numbers. Theoretically, this should be much more efficient than the Tetranacci-like approach.
It is known that the Lucas numbers can computed more efficiently than Fibonacci, therefore we will compute A(n) from the Lucas numbers. The most efficient, simple Lucas number algorithm I know of is one by L.F. Johnson (see his 2010 paper: Middle and Ripple, fast simple O(lg n) algorithms for Lucas Numbers). Once we have a Lucas algorithm, we use the identity: T(n) = L(n - 1) / 5 + V(n) to compute A(n).
# O(log n) integer multiplications, additions, subtractions
def A_by_lucas(n):
n += 3 # because A(n) = T(n+3)
offset = (+1, -2, -1, +2)[n % 4]
L = lf_johnson_2010_middle(n - 1)
return (L + offset) // 5
def lf_johnson_2010_middle(n):
"-> n'th Lucas number. See [L.F. Johnson 2010a]."
#: The following Lucas identities are used:
#:
#: L(2n) = L(n)^2 - 2*(-1)^n
#: L(2n+1) = L(2n+2) - L(2n)
#: L(2n+2) = L(n+1)^2 - 2*(-1)^(n+1)
#:
#: The first and last identities are equivalent.
#: For the unrolled iteration, the following is also used:
#:
#: L(2n+1) = L(n)*L(n+1) - (-1)^n
#:
#: Since this approach uses only square multiplications per loop,
#: It turns out to be slightly faster than standard Lucas doubling,
#: which uses 1 square and 1 regular multiplication.
if n >= 0:
a, b, sign = 2, 1, +1 # L(0), L(1), (-1)^0
else: # n < 0
a, b, sign = -1, 2, -1 # L(-1), L(0), (-1)^(-1)
# unroll the last iteration to avoid computing unnecessary values
for i in reversed(range(1, abs(n).bit_length())):
a = a*a - 2*sign # L(2k)
c = b*b + 2*sign # L(2k+2)
b = c - a # L(2k+1)
sign = +1
if (n >> i) & 1:
a, b = b, c
sign = -1
if n & 1:
return a*b - sign
else:
return a*a - 2*sign
You may verify that A_by_lucas produces the same results as the previous A_by_doubling function, but is roughly 5x faster. Still not fast enough to compute A(10**12) in any reasonable amount of time!
You can easily improve your current recursion implementation by adding memoization which makes the solution fast again. C# code:
// Dictionary to store computed values
private static Dictionary<int, long> s_Solutions = new Dictionary<int, long>();
private static long Count134(int value) {
if (value == 0)
return 1;
else if (value <= 0)
return 0;
long result;
// Improvement: Do we have the value computed?
if (s_Solutions.TryGetValue(value, out result))
return result;
result = Count134(value - 4) +
Count134(value - 3) +
Count134(value - 1);
// Improvement: Store the value computed for future use
s_Solutions.Add(value, result);
return result;
}
And so you can easily call
Console.Write(Count134(500));
The outcome (which takes about 2 milliseconds) is
3350159379832610737
I saw this in an algorithm textbook. I am confused about the middle recursive function. If you can explain it with an example, such as 4/2, that would be great!
function divide(x, y)
Input: Two n-bit integers x and y, where y ≥ 1
Output: The quotient and remainder of x divided by y
if x = 0: return (q, r) = (0, 0)
(q, r) = divide(floor(x/2), y)
q = 2 · q, r = 2 · r
if x is odd: r = r + 1
if r ≥ y: r = r − y, q = q + 1
return (q, r)
You're seeing how many times it's divisible by 2. This is essentially performing bit shifts and operating on the binary digits. A more interesting case would be 13/3 (13 is 1101 in binary).
divide(13, 3) // initial binary value - 1101
divide(6, 3) // shift right - 110
divide(3, 3) // shift right - 11
divide(1, 3) // shift right - 1 (this is the most significant bit)
divide(0, 3) // shift right - 0 (no more significant bits)
return(0, 0) // roll it back up
return(0, 1) // since x is odd (1)
return(1, 0) // r = r * 2 = 2; x is odd (3) so r = 3 and the r > y condition is true
return(2, 0) // q = 2 * 1; r = 2 * 1 - so r >= y and q = 2 + 1
return(4, 1) // q = 2 * 2; x is odd to r = 0 + 1
i wanted ask if there some algorithm ready, that allowed me to do this: i have a matrix m (col) x n (row) with m x n elements. I want give position to this element starting from center and rotating as a spiral, for example, for a matrix 3x3 i have 9 elements so defined:
5 6 7
4 9 8
3 2 1
or for una matrix 4 x 3 i have 12 elements, do defined:
8 9 10 1
7 12 11 2
6 5 4 3
or again, a matrix 5x2 i have 10 elements so defined:
3 4
7 8
10 9
6 5
2 1
etc.
I have solved basically defining a array of integer of m x n elements and loading manually the value, but in generel to me like that matrix maked from algorithm automatically.
Thanks to who can help me to find something so, thanks very much.
UPDATE
This code, do exactely about i want have, but not is in delphi; just only i need that start from 1 and not from 0. Important for me is that it is valid for any matrics m x n. Who help me to translate it in delphi?
(defun spiral (rows columns)
(do ((N (* rows columns))
(spiral (make-array (list rows columns) :initial-element nil))
(dx 1) (dy 0) (x 0) (y 0)
(i 0 (1+ i)))
((= i N) spiral)
(setf (aref spiral y x) i)
(let ((nx (+ x dx)) (ny (+ y dy)))
(cond
((and (< -1 nx columns)
(< -1 ny rows)
(null (aref spiral ny nx)))
(setf x nx
y ny))
(t (psetf dx (- dy)
dy dx)
(setf x (+ x dx)
y (+ y dy)))))))
> (pprint (spiral 6 6))
#2A ((0 1 2 3 4 5)
(19 20 21 22 23 6)
(18 31 32 33 24 7)
(17 30 35 34 25 8)
(16 29 28 27 26 9)
(15 14 13 12 11 10))
> (pprint (spiral 5 3))
#2A ((0 1 2)
(11 12 3)
(10 13 4)
(9 14 5)
(8 7 6))
Thanks again very much.
Based on the classic spiral algorithm. supporting non-square matrix:
program SpiralMatrix;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMatrix = array of array of Integer;
procedure PrintMatrix(const a: TMatrix);
var
i, j: Integer;
begin
for i := 0 to Length(a) - 1 do
begin
for j := 0 to Length(a[0]) - 1 do
Write(Format('%3d', [a[i, j]]));
Writeln;
end;
end;
var
spiral: TMatrix;
i, m, n: Integer;
row, col, dx, dy,
dirChanges, visits, temp: Integer;
begin
m := 3; // columns
n := 3; // rows
SetLength(spiral, n, m);
row := 0;
col := 0;
dx := 1;
dy := 0;
dirChanges := 0;
visits := m;
for i := 0 to n * m - 1 do
begin
spiral[row, col] := i + 1;
Dec(visits);
if visits = 0 then
begin
visits := m * (dirChanges mod 2) + n * ((dirChanges + 1) mod 2) - (dirChanges div 2) - 1;
temp := dx;
dx := -dy;
dy := temp;
Inc(dirChanges);
end;
Inc(col, dx);
Inc(row, dy);
end;
PrintMatrix(spiral);
Readln;
end.
3 x 3:
1 2 3
8 9 4
7 6 5
4 x 3:
1 2 3 4
10 11 12 5
9 8 7 6
2 x 5:
1 2
10 3
9 4
8 5
7 6
There you go!!! After 30some syntax errors...
On ideone.com, I ran it with some tests and it seems to work fine. I think you can see the output there still and run it yourself...
I put some comments in the code. Enough to understand most of it. The main navigation system is a little bit harder to explain. Briefly, doing a spiral is going in first direction 1 time, second 1 time, third 2 times, fourth 2 times, fifth 3 times, 3, 4, 4, 5, 5, and so on. I use what I called a seed and step to get this behavior.
program test;
var
w, h, m, n, v, d : integer; // Matrix size, then position, then value and direction.
spiral : array of array of integer; // Matrix/spiral itself.
seed, step : integer; // Used to travel the spiral.
begin
readln(h);
readln(w);
setlength(spiral, h, w);
v := w * h; // Value to put in spiral.
m := trunc((h - 1) / 2); // Finding center.
n := trunc((w - 1) / 2);
d := 0; // First direction is right.
seed := 2;
step := 1;
// Travel the spiral.
repeat
// If in the sub-spiral, store value.
if ((m >= 0) and (n >= 0) and (m < h) and (n < w)) then
begin
spiral[m, n] := v;
v := v - 1;
end;
// Move!
case d of
0: n := n + 1;
1: m := m - 1;
2: n := n - 1;
3: m := m + 1;
end;
// Plan trajectory.
step := step - 1;
if step = 0 then
begin
d := (d + 1) mod 4;
seed := seed + 1;
step := trunc(seed / 2);
end;
until v = 0;
// Print the spiral.
for m := 0 to (h - 1) do
begin
for n := 0 to (w - 1) do
begin
write(spiral[m, n], ' ');
end;
writeln();
end;
end.
If you really need that to print text spirals I'll let you align the numbers. Just pad them with spaces.
EDIT:
Was forgetting... In order to make it work on ideone, I put the parameters on 2 lines as input. m, then n.
For example:
5
2
yields
3 4
7 8
10 9
6 5
2 1
Here's the commented JavaScript implementation for what you're trying to accomplish.
// return an array representing a matrix of size MxN COLxROW
function spiralMatrix(M, N) {
var result = new Array(M * N);
var counter = M * N;
// start position
var curCol = Math.floor((M - 1) / 2);
var curRow = Math.floor(N / 2);
// set the center
result[(curRow * M) + curCol] = counter--;
// your possible moves RIGHT, UP, LEFT, DOWN * y axis is flipped
var allMoves = [[1,0], [0,-1], [-1,0], [0,1]];
var curMove = 0;
var moves = 1; // how many times to make current Move, 1,1,2,2,3,3,4,4 etc
// spiral
while(true) {
for(var i = 0; i < moves; i++) {
// move in a spiral outward counter clock-wise direction
curCol += allMoves[curMove][0];
curRow += allMoves[curMove][1];
// naively skips locations that are outside of the matrix bounds
if(curCol >= 0 && curCol < M && curRow >= 0 && curRow < N) {
// set the value and decrement the counter
result[(curRow * M) + curCol] = counter--;
// if we reached the end return the result
if(counter == 0) return result;
}
}
// increment the number of times to move if necessary UP->LEFT and DOWN->RIGHT
if(curMove == 1 || curMove == 3) moves++;
// go to the next move in a circular array fashion
curMove = (curMove + 1) % allMoves.length;
}
}
The code isn't the most efficient, because it walks the spiral naively without first checking if the location it's walking on is valid. It only checks the validity of the current location right before it tries to set the value on it.
Even though the question is already answered, this is an alternative solution (arguably simpler).
The solution is in python (using numpy for bidimendional arrays), but can be easily ported.
The basic idea is to use the fact that the number of steps is known (m*n) as end condition,
and to properly compute the next element of the loop at each iteration:
import numpy as np
def spiral(m, n):
"""Return a spiral numpy array of int with shape (m, n)."""
a = np.empty((m, n), int)
i, i0, i1 = 0, 0, m - 1
j, j0, j1 = 0, 0, n - 1
for k in range(m * n):
a[i, j] = k
if i == i0 and j0 <= j < j1: j += 1
elif j == j1 and i0 <= i < i1: i += 1
elif i == i1 and j0 < j <= j1: j -= 1
elif j == j0 and 1 + i0 < i <= i1: i -= 1
else:
i0 += 1
i1 -= 1
j0 += 1
j1 -= 1
i, j = i0, j0
return a
And here some outputs:
>>> spiral(3,3)
array([[0, 1, 2],
[7, 8, 3],
[6, 5, 4]])
>>> spiral(4,4)
array([[ 0, 1, 2, 3],
[11, 12, 13, 4],
[10, 15, 14, 5],
[ 9, 8, 7, 6]])
>>> spiral(5,4)
array([[ 0, 1, 2, 3],
[13, 14, 15, 4],
[12, 19, 16, 5],
[11, 18, 17, 6],
[10, 9, 8, 7]])
>>> spiral(2,5)
array([[0, 1, 2, 3, 4],
[9, 8, 7, 6, 5]])