Multiple nested for loops - wolfram-mathematica

I am programming in Mathematica and I am trying to avoid multiple for loops.
Let n be a given integer and f a function which takes an n-tuple. Given the bound k, I am looking for an efficient way to loop over all n-tuples where each entry ranges from -k to k. (I would be plugging the n-tuples in to f.)
I tried Mathematica's function Tuples[Range[-k,k],n] except I quite often want n to be around 8, 10, or even 15, and even with k as small as 2, (I would like k to be at least 4 or 5) the memory will run out trying to hold (2k+1)^n tuples.
I originally used n for loops, and it did work. Now I want to vary n, and I can't keep manually going in and inserting code for each n.
Thanks for any help and advice you can give!

This is a way to programatically build up nested loops:
ftup[tup_] := Print[tup]
n = 2
k = 1
Do ## Join[{Unevaluated[ftup[Array[ a, n]]]},
Table[ { a[i], -k, k } , {i, n }]]
This shows we are replicating the built in tuples:
ftup[tup_] := Sow[tup];
n = 6;
k = 3;
Last#Last#Reap[ Do ##
Join[{Unevaluated[ftup[Array[ a, n]]]},Table[ { a[i], -k, k } , {i, n }]] ]
== Tuples[ Range[-k, k] , {n}]

I think you only need one loop. Here's one that prints all necessary tuples for you:
With[{k = 2, n = 3}, Do[Print[IntegerDigits[i, 2 k + 1, n] - k], {i, 0, (2 k + 1)^n - 1}]];

Related

Efficient way to find min(max(A[L], A[L+1],...,A[R]), min(B[L], B[L+1],…, B[R]))

Given 2 array A[N] and B[N]. For each 0 <= L < N <= 5e5, find the maximum value of
min(max(A[L], A[L+1],...,A[R]), min(B[L], B[L+1],…, B[R]))
for L <= R <= N.
ans[L] is the answer for L.
For example,
N = 3
A[3] = {3, 2, 1}
B[3] = {3, 2, 3}
So, the answer is
ans[0] = 3
ans[1] = 2
ans[2] = 1
It is clear that brute-forces can run fast.
Then, I tried using Sparse table, Segment Tree or Binary Indexed Tree (and we don't need to update anything, so I choose Sparse Table). But for each L, we don't know R, so I need to run to the end of the array, and this doesn't different from brute-forces .
Is there any efficient algorithm or data structures for this problem??
P/s: Sorry for my bad English.
Using Sparse table A is monotone increasing, B is monotone decreasing, so we need to find the crossing point to get the max out of their min ...
pseudo python code untested
stA = SparseTable(A);
stB = SparseTable(B);
for (i in range(len(A))
r = len(B)
l = i
a = stA.max(l,r)
b = stB.min(l,r)
# binary search for crossing point
while (l != r)
m = l + (r-l)//2 # integer division
a = stA.max(l,m)
b = stB.min(l,m)
if (b > a)
l = m + 1
else
r = m
ans[i] = min(a,b) # might be off-by-one m?
max(A[L], A[L+1], ..., A[R]) is non-increasing in L and non-decreasing in R. Conversely, min(B[L], B[L+1], ..., B[R]) is non-decreasing in L and non-increasing in R. It follows that the function from L to the argmax in R is non-decreasing. The last ingredient is two queues, one that can report max, one that can report min, to quickly compute the sliding window aggregates.

Counting valid sequences with dynamic programming

I am pretty new to Dynamic Programming, but I am trying to get better. I have an exercise from a book, which asks me the following question (slightly abridged):
You want to construct sequence of length N from numbers from the set {1, 2, 3, 4, 5, 6}. However, you cannot place the number i (i = 1, 2, 3, 4, 5, 6) more than A[i] times consecutively, where A is a given array. Given the sequence length N (1 <= N <= 10^5) and the constraint array A (1 <= A[i] <= 50), how many sequences are possible?
For instance if A = {1, 2, 1, 2, 1, 2} and N = 2, this would mean you can only have one consecutive 1, two consecutive 2's, one consecutive 3, etc. Here, something like "11" is invalid since it has two consecutive 1's, whereas something like "12" or "22" are both valid. It turns out that the actual answer for this case is 33 (there are 36 total two-digit sequences, but "11", "33", and "55" are all invalid, which gives 33).
Somebody told me that one way to solve this problem is to use dynamic programming with three states. More specifically, they say to keep a 3d array dp(i, j, k) with i representing the current position we are at in the sequence, j representing the element put in position i - 1, and k representing the number of times that this element has been repeated in the block. They also told me that for the transitions, we can put in position i every element different from j, and we can only put j in if A[j] > k.
It all makes sense to me in theory, but I've been struggling with implementing this. I have no clue how to begin with the actual implementation other than initializing the matrix dp. Typically, most of the other exercises had some sort of "base case" that were manually set in the matrix, and then a loop was used to fill in the other entries.
I guess I am particularly confused because this is a 3D array.
For a moment let's just not care about the array. Let's implement this recursively. Let dp(i, j, k) be the number of sequences with length i, last element j, and k consecutive occurrences of j at the end of the array.
The question now becomes how do we write the solution of dp(i, j, k) recursively.
Well we know that we are adding a j the kth time, so we have to take each sequence of length i - 1, and has j occurring k - 1 times, and add another j to that sequence. Notice that this is simply dp(i - 1, j, k - 1).
But what if k == 1? If that's the case we can add one occurence of j to every sequence of length i - 1 that doesn't end with j. Essentially we need the sum of all dp(i, x, k), such that A[x] >= k and x != j.
This gives our recurrence relation:
def dp(i, j, k):
# this is the base case, the number of sequences of length 1
# one if k is valid, otherwise zero
if i == 1: return int(k == 1)
if k > 1:
# get all the valid sequences [0...i-1] and add j to them
return dp(i - 1, j, k - 1)
if k == 1:
# get all valid sequences that don't end with j
res = 0
for last in range(len(A)):
if last == j: continue
for n_consec in range(1, A[last] + 1):
res += dp(i - 1, last, n_consec)
return res
We know that our answer will be all valid subsequences of length N, so our final answer is sum(dp(N, j, k) for j in range(len(A)) for k in range(1, A[j] + 1))
Believe it or not this is the basis of dynamic programming. We just broke our main problem down into a set of subproblems. Of course, right now our time is exponential because of the recursion. We have two ways to lower this:
Caching, we can simply keep track of the result of each (i, j, k) and then spit out what we originally computed when it's called again.
Use an array. We can reimplement this idea with bottom-up dp, and have an array dp[i][j][k]. All of our function calls just become array accesses in a for loop. Note that using this method forces us iterate over the array in topological order which may be tricky.
There are 2 kinds of dp approaches: top-down and bottom-up
In bottom up, you fill the terminal cases in dp table and then use for loops to build up from that. Lets consider bottom-up algo to generate Fibonacci sequence. We set dp[0] = 1 and dp[1] = 1 and run a for loop from i = 2 to n.
In top down approach, we start from the "top" view of the problem and go down from there. Consider the recursive function to get n-th Fibonacci number:
def fib(n):
if n <= 1:
return 1
if dp[n] != -1:
return dp[n]
dp[n] = fib(n - 1) + fib(n - 2)
return dp[n]
Here we don't fill the complete table, but only the cases we encounter.
Why I am talking about these 2 types is because when you start learning dp, it is often difficult to come up with bottom-up approaches (like you are trying to). When this happens, first you want to come up with a top-down approach, and then try to get a bottom up solution from that.
So let's create a recursive dp function first:
# let m be size of A
# initialize dp table with all values -1
def solve(i, j, k, n, m):
# first write terminal cases
if k > A[j]:
# this means sequence is invalid. so return 0
return 0
if i >= n:
# this means a valid sequence.
return 1
if dp[i][j][k] != -1:
return dp[i][j][k]
result = 0
for num = 1 to m:
if num == j:
result += solve(i + 1, num, k + 1, n)
else:
result += solve(i + 1, num, 1, n)
dp[i][j][k] = result
return dp[i][j][k]
So we know what terminal cases are. We create a dp table of size dp[n + 1][m][50]. Initialize it with all values 0, not -1.
So we can do bottom-up as:
# initially all values in table are zero. With loop below, we set the valid endings as 1.
# So any state trying to reach valid terminal states will get 1, but invalid states will
# return the values 0
for num = 1 to m:
for occour = 1 to A[num]:
dp[n][num][occour] = 1
# now to build up from bottom, we start by filling n-1 th position
for i = n-1 to 1:
for num = 1 to m:
for occour = 1 to A[num]:
for next_num = 1 to m:
if next_num != num:
dp[i][num][occour] += dp[i + 1][next_num][1]
else:
dp[i][num][occour] += dp[i + 1][num][occour + 1]
The answer will be:
sum = 0
for num = 1 to m:
sum += dp[1][num][1]
I am sure there must be some more elegant dp solution, but I believe this answers your question. Note that I considered that k is the number of times j-th number has been repeated consecutively, correct me if I am wrong with this.
Edit:
With the given constraints the size of the table will be, in the worst case, 10^5 * 6 * 50 = 3e7. This would be > 100MB. It is workable, but can be considered too much space use (I think some kernels doesn't allow that much stack space to a process). One way to reduce it would be to use a hash-map instead of an array with top down approach since top-down doesn't visit all the states. That would be mostly true in this case, for example if A[1] is 2, then all the other states where 1 has occoured more that twice need not be stored. Ofcourse this would not save much space if A[i] has large values, say [50, 50, 50, 50, 50, 50]. Another approach would be to modify our approach a bit. We dont actually need to store the dimension k, i.e. the times j has appeared consecutively:
dp[i][j] = no of ways from i-th position if (i - 1)th position didn't have j and i-th position is j.
Then, we would need to modify our algo to be like:
def solve(i, j):
if i == n:
return 1
if i > n:
return 0
if dp[i][j] != -1
return dp[i][j]
result = 0
# we will first try 1 consecutive j, then 2 consecutive j's then 3 and so on
for count = 1 to A[j]:
for num = 1 to m:
if num != j:
result += solve(i + count, num)
dp[i][j] = result
return dp[i][j]
This approach will reduce our space complexity to O(10^6) ~= 2mb, while time complexity is still the same : O(N * 6 * 50)

How to construct one arithmetic free permutation for each n?

For some fixed integer N, an array A[1..N] is an arithmetic-free permutation if
A is a permutation of { 1, ... , N }; and
for every 1 ≤ i < j < k ≤ N, the elements A[i], A[j], A[k] (in order) do NOT form an arithmetic
progression. That means, A[j] - A[i] ≠ A[k] - A[j]
Give an algorithm which, given N, returns an arithmetic-free permutation of size N in O(N log N) time. It is guaranteed that arithmetic-free permutations exist for all positive integers N.
Construct the bit-reversal permutation for the next highest power of two and drop out the numbers that don't belong. There are several ways to do this in O(n log n) time. I'm not going to write a formal proof in case this is homework, but the general idea is to look at the lowest order bit where A[i], A[j], and A[k] are not all the same and observe that the two that agree are adjacent.
There is a good answer at https://leetcode.com/articles/beautiful-array/
Say a < b < c and b - a = c - b. Then
2 * b = a + c
Since 2 * b is always even, to break any progression, a and c must have different parity. But grouping odds to one side and evens on the other is not enough since if we have more than 4 numbers, we could generate an arithmetic progression within one of the groups.
This is where we can use the recursive idea in the article to break it. One way I understand it is to consider that if we have a solution for array size N, because the arithmetic progression depends on differences between the numbers, we can map a given solution by an arithmetic function to the same effect:
if [a, b, c, d] works,
then [2*a, 2*b, 2*c, 2*d] works too
and so does [2*a - 1, 2*b - 1, 2*c - 1, 2*d - 1]
Therefore all we need to do is map a smaller solution once to the even numbers and once to the odds and group them separately. (Separating the groups limits the problem to breaking the arithmetic progression in each group since, as we've shown, no progression, (a, b, c), would rely on a and c of different parities.)
N1 -> [1]
N2 -> even map N1 + odd map N1
[2*1] + [2*1 - 1]
[2, 1]
N3 -> even map N1 + odd map N2
[2*1] + [2*2 - 1, 2*1 - 1]
[2, 3, 1]
...
N6 -> even map N3 + odd map N3
[2*2, 2*3, 2*1] + [2*2 - 1, 2*3 - 1, 2*1 - 1]
[4, 6, 2, 3, 5, 1]

Algorithm for finding combinations of an variable length subset within an arbitrary length set

I'm trying to work out a method that would return a list of all combinations of a subset within a set.The method call would look something like getSubsets(7, 3) based on the image below, the returned output I need would be in the form and order of:
123
124
125
126
127
134
135
136
137
145
... and so on
The image below shows exactly how the counting order should go. I've been banging my head on this one for a day can't find a good solution. TIA.
To get the next subset after a given subset:
Find the largest element in the subset whose successor is not in the subset.
Remove that element and all larger elements in the subset.
Starting with the successor element, add successive elements until the subset is the correct size.
If step 1 fails, you have enumerated all possible subsets.
In C:
bool next_subset(int* subset, int n, int k) {
// subset is a vector of k ints in the range [0, n)
int i, j;
for (i = k - 1, j = n - 1; subset[i] == j; --i, --j) {
if (i == 0)
return false; // No more subsets
}
for (j = subset[i] + 1; i < k ; ++i, ++j) {
subset[i] = j;
}
return true;
}
If you know m of getSubsets(n, m) in advance, the code is just nested for-looping; eg. in Mathematica:
With[{n = 7},
Table[{i, j, k},
{i, n - 2}, {j, i + 1, n - 1}, {k, j + 1, n}]]
This produces all 3-subsets in the order you want, with n a parameter. Of course, if m is anything else than 3, the code is useless, specific code is needed for specific m. This is why I favor the flexible approach from #rici which is valid for general m.

Sum of continuous sequences

Given an array A with N elements, I want to find the sum of minimum elements in all the possible contiguous sub-sequences of A. I know if N is small we can look for all possible sub sequences but as N is upto 10^5 what can be best way to find this sum?
Example: Let N=3 and A[1,2,3] then ans is 10 as Possible contiguous sub sequences {(1),(2),(3),(1,2),(1,2,3),(2,3)} so Sum of minimum elements = 1 + 2 + 3 + 1 + 1 + 2 = 10
Let's fix one element(a[i]). We want to know the position of the rightmost element smaller than this one located to the left from i(L). We also need to know the position of the leftmost element smaller than this one located to the right from i(R).
If we know L and R, we should add (i - L) * (R - i) * a[i] to the answer.
It is possible to precompute L and R for all i in linear time using a stack. Pseudo code:
s = new Stack
L = new int[n]
fill(L, -1)
for i <- 0 ... n - 1:
while !s.isEmpty() && s.top().first > a[i]:
s.pop()
if !s.isEmpty():
L[i] = s.top().second
s.push(pair(a[i], i))
We can reverse the array and run the same algorithm to find R.
How to deal with equal elements? Let's assume that a[i] is a pair <a[i], i>. All elements are distinct now.
The time complexity is O(n).
Here is a full pseudo code(I assume that int can hold any integer value here, you should
choose a feasible type to avoid an overflow in a real code. I also assume that all elements are distinct):
int[] getLeftSmallerElementPositions(int[] a):
s = new Stack
L = new int[n]
fill(L, -1)
for i <- 0 ... n - 1:
while !s.isEmpty() && s.top().first > a[i]:
s.pop()
if !s.isEmpty():
L[i] = s.top().second
s.push(pair(a[i], i))
return L
int[] getRightSmallerElementPositions(int[] a):
R = getLeftSmallerElementPositions(reversed(a))
for i <- 0 ... n - 1:
R[i] = n - 1 - R[i]
return reversed(R)
int findSum(int[] a):
L = getLeftSmallerElementPositions(a)
R = getRightSmallerElementPositions(a)
int res = 0
for i <- 0 ... n - 1:
res += (i - L[i]) * (R[i] - i) * a[i]
return res
If the list is sorted, you can consider all subsets for size 1, then 2, then 3, to N. The algorithm is initially somewhat inefficient, but an optimized version is below. Here's some pseudocode.
let A = {1, 2, 3}
let total_sum = 0
for set_size <- 1 to N
total_sum += sum(A[1:N-(set_size-1)])
First, sets with one element:{{1}, {2}, {3}}: sum each of the elements.
Then, sets of two element {{1, 2}, {2, 3}}: sum each element but the last.
Then, sets of three elements {{1, 2, 3}}: sum each element but the last two.
But this algorithm is inefficient. To optimize to O(n), multiply each ith element by N-i and sum (indexing from zero here). The intuition is that the first element is the minimum of N sets, the second element is the minimum of N-1 sets, etc.
I know it's not a python question, but sometimes code helps:
A = [1, 2, 3]
# This is [3, 2, 1]
scale = range(len(A), 0, -1)
# Take the element-wise product of the vectors, and sum
sum(a*b for (a,b) in zip(A, scale))
# Or just use the dot product
np.dot(A, scale)

Resources