What I try to do is to check a given array is ordered in an ascending manner by divide-and-conquer approach.
I wonder what the logic behind the additional return case (a⌊n/2⌋−1 ≤ a⌊n/2⌋) is to reach to the final result. I attempted to solve the problem without looking at its solution but I couldn't figure out how the author discovers/devises a⌊n/2⌋−1 ≤ a⌊n/2⌋ case. It is really hard for me to unearth the case.
Actually, Why not a⌊n/2⌋ ≤ a⌊n/2⌋+1 instead? And the base case case, why I'm stackoverflowed when I remove equality from the base case, that is h<l?
with trial and error approach, I tried to write the following.
def co(a, l, h):
if h <= l:
return True
mid = l + ((h-l)//2)
cl = co(a, l, mid-1)
rl = co(a, mid+1, h)
return rl and cl and a[mid] < a[mid+1]
# how is a[mid] < a[mid+1] devised ??
# why not a[mid-1] < a[mid] ??
#c = [3, 5, 7, 9, 11,12]
c = [3, 5]
print(co(c, 0, len(c) - 1))
And the base case case, why I'm stackoverflowed when I remove equality
from the base case, that is h<l?
Assume c=[3, 5]. If you replace h<=l with h<l, then when you compute co(a, 1, 1), then mid = 1+0 ... then rl = co (a, 1+1, 1) and a[2] gives you stackoverflow.
How is a[mid] <= a[mid+1] devised ??
You need to compare the most-right element of subproblem1 with the most-left element of subproblem2. The order of these two elements are not taken into account in subproblem1 and subproblem2.
Be careful with Python indexing. 1) When you split the list into a[l:mid-1] and a[mid+1,h], you leave out a[mid-1] and a[mid]. 2) When you write co(c, 0, len(c) - 1) you leave out the last element of c (see Comment4).
There are some mistakes in your code, see my comments.
def co(a, l, h):
if h <= l:
return True
mid = l + ((h-l)//2)
cl = co(a, l, mid-1)
rl = co(a, mid+1, h)
return rl and cl and a[mid] < a[mid+1] ### Comment1: this misses checking a[mid-1] <= a[mid]
# how is a[mid] < a[mid+1] devised ?? ### Comment2: you should use <= rather than <
# why not a[mid-1] < a[mid] ??
#c = [12, 3, 5, 7, 9, 11,12] ### Comment3: your code returns True for this unordered list!
#c = [3, 5, 7, 9, 11,12]
c = [3, 5]
print(co(c, 0, len(c) - 1)) ### Comment4: len(c)-1 should be len(c) otherwise it's not the whole list
Below, I fixed the list indexing in your code. Note that the test becomes h <= l+1 because in Python the list a[mid:mid+1] contains one element.
def co(a, l, h):
if h <= l+1:
return True
mid = l + ((h-l)//2)
cl = co(a, l, mid)
rl = co(a, mid, h)
return rl and cl and a[mid-1] <= a[mid]
Related
Given an integer n, and 2 real sequences {a_1, ..., a_n} and {b_1, ..., b_n}, with a_i, b_i > 0, for all i. For a given fixed m < n let {P_1, ..., P_m} be a partition of the set {1, ..., n} as in P_1 U ... U P_n = {1, ..., n}, with the P_i's pairwise disjoint (empty intersection). I wish to find a partition of size m that maximizes the expression
The number of partitions of the set is n choose m, prohibitively large to do by brute force. Is there an iterative or approximate solution that does better?
For insight into this problem the code block at the end solves via brute-force. For realistic size problems (n ~ 1e6, k ~ 20) it is unusable as is, but easily distrubuted.
Edit: Presorting a, b by the values of a^2/b always gives increasing partition indices:
a = rng.uniform(low=0.0, high=10.0, size=NUM_POINTS)
b = rng.uniform(low=0.0, high=10.0, size=NUM_POINTS)
ind = np.argsort(a/b)
(a,b) = (seq[ind] for seq in (a,b))
a sample run with
NUM_POINTS = 16
PARTITION_SIZE = 3
gives an optimal partition of
[[0, 1, 2, 3, 4, 5, 6, 7], [8, 9], [10, 11]]
which is monotonic in the indices. I think I can prove this. If so, the brute-force search could be improved to n choose k-1 time, still long, but a significant savings.
import numpy as np
import multiprocessing
import concurrent.futures
from functools import partial
from itertools import islice
rng = np.random.RandomState(55)
def knuth_partition(ns, m):
def visit(n, a):
ps = [[] for i in range(m)]
for j in range(n):
ps[a[j + 1]].append(ns[j])
return ps
def f(mu, nu, sigma, n, a):
if mu == 2:
yield visit(n, a)
else:
for v in f(mu - 1, nu - 1, (mu + sigma) % 2, n, a):
yield v
if nu == mu + 1:
a[mu] = mu - 1
yield visit(n, a)
while a[nu] > 0:
a[nu] = a[nu] - 1
yield visit(n, a)
elif nu > mu + 1:
if (mu + sigma) % 2 == 1:
a[nu - 1] = mu - 1
else:
a[mu] = mu - 1
if (a[nu] + sigma) % 2 == 1:
for v in b(mu, nu - 1, 0, n, a):
yield v
else:
for v in f(mu, nu - 1, 0, n, a):
yield v
while a[nu] > 0:
a[nu] = a[nu] - 1
if (a[nu] + sigma) % 2 == 1:
for v in b(mu, nu - 1, 0, n, a):
yield v
else:
for v in f(mu, nu - 1, 0, n, a):
yield v
def b(mu, nu, sigma, n, a):
if nu == mu + 1:
while a[nu] < mu - 1:
yield visit(n, a)
a[nu] = a[nu] + 1
yield visit(n, a)
a[mu] = 0
elif nu > mu + 1:
if (a[nu] + sigma) % 2 == 1:
for v in f(mu, nu - 1, 0, n, a):
yield v
else:
for v in b(mu, nu - 1, 0, n, a):
yield v
while a[nu] < mu - 1:
a[nu] = a[nu] + 1
if (a[nu] + sigma) % 2 == 1:
for v in f(mu, nu - 1, 0, n, a):
yield v
else:
for v in b(mu, nu - 1, 0, n, a):
yield v
if (mu + sigma) % 2 == 1:
a[nu - 1] = 0
else:
a[mu] = 0
if mu == 2:
yield visit(n, a)
else:
for v in b(mu - 1, nu - 1, (mu + sigma) % 2, n, a):
yield v
n = len(ns)
a = [0] * (n + 1)
for j in range(1, m + 1):
a[n - m + j] = j - 1
return f(m, n, 0, n, a)
def Bell_n_k(n, k):
''' Number of partitions of {1,...,n} into
k subsets, a restricted Bell number
'''
if (n == 0 or k == 0 or k > n):
return 0
if (k == 1 or k == n):
return 1
return (k * Bell_n_k(n - 1, k) +
Bell_n_k(n - 1, k - 1))
NUM_POINTS = 13
PARTITION_SIZE = 4
NUM_WORKERS = multiprocessing.cpu_count()
INT_LIST= range(0, NUM_POINTS)
REPORT_EACH = 10000
partitions = knuth_partition(INT_LIST, PARTITION_SIZE)
# Theoretical number of partitions, for accurate
# division of labor
num_partitions = Bell_n_k(NUM_POINTS, PARTITION_SIZE)
bin_ends = list(range(0,num_partitions,int(num_partitions/NUM_WORKERS)))
bin_ends = bin_ends + [num_partitions] if num_partitions/NUM_WORKERS else bin_ends
islice_on = list(zip(bin_ends[:-1], bin_ends[1:]))
# Have to consume it; can't split work on generator
partitions = list(partitions)
rng.shuffle(partitions)
slices = [list(islice(partitions, *ind)) for ind in islice_on]
return_values = [None] * len(slices)
futures = [None] * len(slices)
a = rng.uniform(low=0.0, high=10.0, size=NUM_POINTS)
b = rng.uniform(low=0.0, high=10.0, size=NUM_POINTS)
ind = np.argsort(a/b)
(a,b) = (seq[ind] for seq in (a,b))
def start_task():
print('Starting ', multiprocessing.current_process().name)
def _task(a, b, partitions, report_each=REPORT_EACH):
max_sum = float('-inf')
arg_max = -1
for ind,part in enumerate(partitions):
val = 0
for p in part:
val += sum(a[p])**2/sum(b[p])
if val > max_sum:
max_sum = val
arg_max = part
if not ind%report_each:
print('Percent complete: {:.{prec}f}'.
format(100*len(slices)*ind/num_partitions, prec=2))
return (max_sum, arg_max)
def reduce(return_values):
return max(return_values, key=lambda x: x[0])
task = partial(_task, a, b)
with concurrent.futures.ThreadPoolExecutor() as executor:
for ind,slice in enumerate(slices):
futures[ind] = executor.submit(task, slice)
return_values[ind] = futures[ind].result()
reduce(return_values)
I'm trying to simply re-phrase the problem with sample input, let me know if I missed anything.
A = [1, 3, 2, 1, 4]
B = [2, 1, 5, 3, 1]
n = length(A) = length(B) = 5
We have two lists with positive integers.
We need to find a set of indices S (a subset of N = {1,2,3,..n}), let's assume it's {2,3,5}. Now, we get a new set S' = N - S = {1, 4}
For S and S', (sum(A[S]))^2/(sum(B[S'])) needs to be maximized.
As you said, the approximation solution will work too. One of the heuristics we can use is we need to choose such S so that the values of A list is high and values of B list is
low.
As we take the square of the sum on the subset of A, let's just sort A and choose a sublist so that we get the max score.
import numpy as np
A = np.array([1, 2, 3, 4, 1, 2, 3])
B = np.array([3, 3, 1, 2, 1, 3, 1])
sorted_idx = sorted(range(len(A)), key=lambda k: A[k]) # also other sorting strategy can be used, A[k]/B[k]
A_p = A[sorted_idx]
B_p = B[sorted_idx]
max_s = 0
part_ans = -1
for i in range(len(A_p)):
cur_s = (sum(A_p[:i])**2)/sum(B_p[i:])
if cur_s >= max_s:
print(cur_s)
max_s = cur_s
part_ans = i
print(f'The partitions are: {sorted_idx[:i]} and {sorted_idx[i:]}')
Is there any better solution than the obvious O(n^3)? I can use one element multiple times so for an array {-1, 0, 2} there is a solution: (-1, -1, 2) or (0, 0, 0).
The classic 3SUM problem can be solved in O(n^2). This can also be solved with O(n^2)
# assume arr is sorted and doesn't contain duplicates
out = []
s = set(arr)
for i in range(len(arr)):
for j in range(i, len(arr)):
a = - (arr[i] + arr[j])
if a >= arr[j] and a in s:
out.append((arr[i], arr[j], a))
I am attempting to solve, using recursion a way to maximize sub sequence sum such that no three elements are consecutive.
There is a way to do this by dynamic programming but I want to build on it using recursion first.
Some sample input and outputs:
Input:{1, 2, 3}
Output: 5
Input:{100, 1000, 100, 1000, 1}
Output: 2101
Input:{1, 2, 3, 4, 5, 6, 7, 8}
Output: 27
I am able to get mostly correct results apart from the second one {100, 1000, 100, 1000, 1}.
My solution:
int maxSubsequenceSum(vector<int> nums)
{
return helper(nums, nums.size());
}
int helper(vector<int>& nums, int index)
{
if (index <= 0) return 0;
int withoutThird = helper(nums, index - 3) + nums[index - 1] + nums[index - 2];
int withoutSecond = helper(nums, index - 3) + (index - 1 < 0 ? 0 : nums[index - 1]) + (index - 3 < 0 ? 0 : nums[index - 3]);
int withoutFirst = helper(nums, index - 3) + (index - 2 < 0 ? 0 : nums[index - 2]) + (index - 3 < 0 ? 0 : nums[index - 3]);
return max(withoutThird, max(withoutSecond, withoutFirst));
}
Individually the three withoutThird, withoutSecond and withoutFirst give the correct result only whilst in a recursive arrangement it fails. Why does it fail and is this a correct recursive approach?
The problem is to get maximum with no three consecutive elements.
What you are doing is, taking 3 elements at a time, select two having maximum sum from it, add them and so on.
Taking one example :-
Input : {A, B, C, D, E, F}
As your recursion goes right to left.
Assume, taking, {D, E, F}
(D + E) > (E + F) and (D + E) > (D + F)
Your code will select {D, E} from last 3 elements.
Now, taking, {A, B, C} Assuming,
(B + C) > (A + B) and (B + C) > (A + C)
Your code will select {B, C} from first 3 elements.
Total Selected Elements = {B, C, D, E}.
Noticed Something?
You ended up adding four continuous elements.
One short example : {100, 1000, 100, 1000, 1}
2 windows : [0,1] and [2,4]
Selected {100, 1000} from [2, 4]
and Selected {100, 1000} from [0, 1]
Added up four continuos elements.
Got: 2200, which is your actual output.
Hint: Try to pass the element index which u didn't add from one state of recursion to another. If still stuck, comment and I will write a similar code :)
Problem
withoutSecond and withoutFirst has some bugs. To make it simpler, let's assume index >= 3. Look at withoutSecond:
withoutSecond = helper(nums, index - 3) + nums[index - 1] + nums[index - 3]
It picks index-1 and index-3. So if we pick index-4 in helper(nums, index - 3) then we can not pick index-5, but it contains in withoutThird in function helper(nums, index - 3). That would yield a larger result than expected.
Algorithm
As the condition is does not allow 3 consecutive elements. So we only need to consider 2 consecutive elements to decide if we should pick another or not.
Suppose f(a, n) calculates largest result of array a with size n.
If does not pick a[n]: f(a, n) -> f(a, n-1)
If pick a[n] && pick a[n-1]: f(a, n) -> f(a, n-3) + a[n] + a[n-1]
If pick a[n] && not pick a[n-1]: f(a, n) -> f(a, n-2) + a[n]
OK, that's all the 3 cases.
Code
See the following code for details
#include <vector>
#include <cstdio>
using namespace std;
// this runs slow
// but you can use memorized search to speed up the process
int helper(vector<int>& nums, int index) {
if (index == 0) return 0;
if (index == 1) return nums[0];
if (index == 2) return nums[0] + nums[1];
int without_last_1 = helper(nums, index-1);
int with_last_1_and_2 = helper(nums, index-3) + nums[index-1] + nums[index-2];
int with_last_1_and_without_last_2 = helper(nums, index-2) + nums[index-1];
return max(without_last_1, max(with_last_1_and_2, with_last_1_and_without_last_2));
}
int maxSubsequenceSum(vector<int> nums) {
return helper(nums, nums.size());
}
int main() {
printf("%d\n", maxSubsequenceSum({1, 2, 3}));
printf("%d\n", maxSubsequenceSum({100, 1000, 100, 1000, 1}));
printf("%d\n", maxSubsequenceSum({1, 2, 3, 4, 5, 6, 7, 8}));
return 0;
}
You need to climb a staircase that has n steps, and you decide to get some extra exercise by jumping up the steps. You can cover at most k steps in a single jump. Return all the possible sequences of jumps that you could take to climb the staircase, sorted.
My implementation is obviously giving me the wrong answer.
def climbingStaircase(n, k):
final_res=[]
final_res.append(CSR(n,k,[]))
return final_res
def CSR(n,k,res):
if n == 0:
return res
else:
for i in range(1,k+1):
if n-i>=0:
res.append(i)
n=n-i
res=CSR(n,i,res)
return res
For n = 4 and k = 2, the output should be
[[1, 1, 1, 1],
[1, 1, 2],
[1, 2, 1],
[2, 1, 1],
[2, 2]]
Actual output:
[[1,1,1,1,2,1]]
Can someone point out which part I'm missing?
One huge problem is in the code below: you deduct the quantity of steps for each possibility within the step range.
n=n-i
res=CSR(n,i,res)
When you're done exploring what you can do with a 1-step jump, you need to backtrack and try from the same starting point (this instance's original value of n) with a 2-step jump. Change the code to:
res = CSR(n-i, i, res)
This keeps the n value intact as you go through the loop.
In addition, you can't limit future jumps to the max of what you just took. Change that second parameter, too:
res = CSR(n-i, k, res)
That should get you moving. Also try this lovely debug blog for help. At least insert one or two tracing statements, such as
print n, k, res
at the top of your routine.
CAVEAT
This is not all of your trouble. The largest remaining problem is that CSR returns only one solution: every step you take is appended to the same list. You need a way to gather the completed solutions as separate lists; the append in climbingStaircase is executed only once, after CSR is entirely finished.
You need to recognize a completed solution at n==0.
DEBUGGING HELP
Here is a version of your program with the recursion parameters fixed, and debugging traces inserted.
indent = ""
def climbingStaircase(n, k):
final_res = []
final_res.append(CSR(n, k, []))
return final_res
def CSR(n, k, res):
global indent
indent += " "
print indent, n, k, res
if n == 0:
print "SOLUTION", res
else:
for i in range(1, k+1):
if n-i >= 0:
CSR(n-i, k, res + [i])
indent = indent[:-2]
print climbingStaircase(4, 2)
Note the use of "indent" to help visualize your recursion and backtracking. The critical part here is that, instead of updating res globally, I've left it as a local variable. I've also removed the return value for now, simply dumping to output the solutions as they're found. You can see how it works:
4 2 []
3 2 [1]
2 2 [1, 1]
1 2 [1, 1, 1]
0 2 [1, 1, 1, 1]
SOLUTION [1, 1, 1, 1]
0 2 [1, 1, 2]
SOLUTION [1, 1, 2]
1 2 [1, 2]
0 2 [1, 2, 1]
SOLUTION [1, 2, 1]
2 2 [2]
1 2 [2, 1]
0 2 [2, 1, 1]
SOLUTION [2, 1, 1]
0 2 [2, 2]
SOLUTION [2, 2]
[None]
With this stuff in place, I'm hopeful you can trace your logic and figure out how to capture the sequence of solutions at a level of your choosing.
Successfully implemented Prune's answer.
def climbingStaircase(n, k):
res=[]
CSR(n,k,[],res)
return res
def CSR(n,k,str_, res):
if n == 0:
res.append(str_)
else:
for i in range(1,k+1):
if n-i>=0:
CSR(n-i,k,str_+[i],res)
A quick Java version of this solution:
int[][] climbingStaircase(int n, int k) {
List<ArrayList<Integer>> list = new ArrayList<>();
climb(n, k, new ArrayList<Integer>(), list);
// convert to int[][]
int[][] result = new int[list.size()][];
for (int i=0; i<list.size(); i++) {
List<Integer> l = list.get(i);
int [] arr = new int[l.size()];
for (int j=0; j<l.size(); j++)
arr[j] = l.get(j);
result[i] = arr;
}
return result;
}
void climb(int n, int k, ArrayList<Integer> prev, List<ArrayList<Integer>> list) {
if (n==0) { // no more stairs, done climbing
list.add(prev);
} else {
for (int i=1; i<=k; i++) { // climb remaining stairs in intervals from 1 to k steps
if (i <= n) { // no need to test intervals larger than remaining # of stairs
ArrayList<Integer> branch = new ArrayList<>(prev);
branch.add(i);
climb(n-i, k, branch, list);
}
}
}
}
In Swift 5.5
func solution(n: Int, k: Int) -> [[Int]] {
var res_final = [[Int]]()
SRC(n: n, k: k, res: [], &res_final)
return res_final
}
var indent: String = ""
func SRC(n: Int, k: Int, res: [Int], _ res_final: inout [[Int]]) {
indent += " "
print(indent, n, k, res)
if n == .zero {
res_final.append(res)
print("Solution", res)
} else {
for i in 1...k {
if n-i >= .zero {
SRC(n: n-i, k: k, res: res + [i], &res_final)
}
}
}
indent = " "
}
solution(n: 4, k: 2)
This is the first part which I have done already:
Let A and B be sorted arrays with all elements of A distinct and all elements of B distinct (though elements can occur in both A and B). Design an O (n) algorithm that produces a sorted array C containing all elements of A and B without repetitions. For instance, if A = [1, 2, 5, 7] and B = [2, 5, 10], C = [1, 2, 5, 7, 10].
But I'm stuck with this part to do with list:
Solve the same exercise for the case where A and B are linked lists.
My code:
Merge(A,B,C)
i=0;
j=0;
k=0;
while (i < A.length && j < B.length)
if (A.content <= B.content)
C.content = A.content
k = k + 1; i = i + 1
Your algorithm is incomplete: it does not tell what to do when A.content > B.content. The approach is nearly identical to the well-known algorithm for merging two sorted collections, with the exception that when two items are equal you advance both collections.
Using linked lists does not change the algorithm, because in both cases you operate on a single "head" element from each collection.
merge-lists(list a, list b) -> list c
while !a.at-end && !b.at-end
if a.head < b.head
c.add( a.head )
a.move-next
else if a.head > b.head
c.add( b.head )
b.move-next
else // it means that a.head == b.head
c.add( a.head )
a.move-next
b.move-next
end
while !a.at-end
c.add( a.head )
a.move-next
while !b.at-end
c.add( b.head )
b.move-next