Related
How do I find two max value in a list and sum up, not using rec, only can use List.fold_left or right and List.map?
I used filter, but it's not allowed, anyways I can replace the filter?
let max a b =
if b = 0 then a
else if a > b then a
else b;;
let maxl2 lst =
match lst with
| [] -> 0
| h::t ->
let acc = h in
List.fold_left max acc lst +
List.fold_left
max acc
(List.filter (fun x -> (x mod List.fold_left max acc lst) != 0) lst);;
List.fold_left is very powerful and can be used to implement List.filter, List.map, List.rev and so on. So it's not much of a restriction. I would assume the purpose of the exercise is for you to learn about the folds and what they can do.
If your solution with List.filter actually works, you should be able to replace List.filter by one you wrote yourself using List.fold_left. The basic idea of a fold is that it builds up a result (of any type you choose) by looking at one element of the list at a time. For filter, you would add the current element to the result if it passes the test.
However I have to wonder whether your solution will work even with List.filter. I don't see why you're using mod. It doesn't make a lot of sense. You seem to need an equality test (= in OCaml). You can't use mod as an equality test. For example 28 mod 7 = 0 but 28 <> 7.
Also your idea of filtering out the largest value doesn't seem like it would work if the two largest values were equal.
My advice is to use List.fold_left to maintain the two largest values you've seen so far. Then add them up at the end.
To build on what Jeffrey has said, List.fold_left looks at one element in a list at a time and an accumulator. Let's consider a list [1; 3; 7; 0; 6; 2]. An accumulator that makes sense is a tuple with the first element being the largest and the second element representing the second largest. We can initially populate these with the first two elements.
The first two elements of this list are [1; 3]. Finding the max of that we can turn this into the tuple (3, 1). The remainder of the list is [7; 0; 6; 2].
First we consider 7. It's bigger than 3, so we change the accumulator to (7, 3). Next we consider 0. This is smaller than both elements of the accumulator, so we make no changes. Next: 6. This is bigger than 3 but smaller than 7, so we updated the accumulator to (7, 6). Next: 2 which is smaller than both, so no change. The resulting accumulator is (7, 6).
Actually writing the code for this is your job.
Often, functions called by fold use an accumulator that is simple enough to be stored as an anonymous tuple. But this can become hard to understand when you are dealing with complex behaviors: you have to consider different corner cases, like what is the initial accumulator value? what is the regular behavior of the function, ie. when the accumulator has encountered enough values? what happens before that?
For example here you have to keep track of two maximal values (the general case), but your code has a build-up phase where there is only one element being visited in the list, and starts with initially no known max value. This kind of intermediate states is IMO the hardest part of using fold (the more pleasant cases are when the accumulator and list elements are of the same type).
I'd recommend making it very clear what type the accumulator is, and write as many helper functions as possible to clear things up.
To that effect, let's define the accumulator type as follows, with all different cases treated explicitly:
type max_of_acc =
| SortedPair of int * int (* invariant: fst <= snd *)
| Single of int
| Empty
Note that this isn't the only way to do it, you could keep a list of maximum values, initially empty, always sorted, and of size at most N, for some N (and then you would solve a more general case, ie. a list of N highest values). But as an exercise, it helps to cover the different cases as above.
For example, at some point you will need to compute the sum of the max values.
let sum_max_of m = match m with
| Empty -> 0
| Single v -> v
| SortedPair (u,v) -> u+v;;
I would also define the following helper function:
let sorted_pair u v = if u <= v then SortedPair (u,v) else SortedPair (v, u)
Finally, your function would look like this:
let fold_max_of acc w = match acc with
| Empty -> ...
| Single v -> ...
| SortedPair (u, v) -> ...
And could be used in the following way:
# List.fold_left fold_max_of Empty [1;2;3;5;4];;
- : max_of = SortedPair (4, 5)
I am looking for a least time-complex algorithm that would solve a variant of the perfect sum problem (initially: finding all variable size subset combinations from an array [*] of integers of size n that sum to a specific number x) where the subset combination size is of a fixed size k and return the possible combinations without direct and also indirect (when there's a combination containing the exact same elements from another in another order) duplicates.
I'm aware this problem is NP-hard, so I am not expecting a perfect general solution but something that could at least run in a reasonable time in my case, with n close to 1000 and k around 10
Things I have tried so far:
Finding a combination, then doing successive modifications on it and its modifications
Let's assume I have an array such as:
s = [1,2,3,3,4,5,6,9]
So I have n = 8, and I'd like x = 10 for k = 3
I found thanks to some obscure method (bruteforce?) a subset [3,3,4]
From this subset I'm finding other possible combinations by taking two elements out of it and replacing them with other elements that sum the same, i.e. (3, 3) can be replaced by (1, 5) since both got the same sum and the replacing numbers are not already in use. So I obtain another subset [1,5,4], then I repeat the process for all the obtained subsets... indefinitely?
The main issue as suggested here is that it's hard to determine when it's done and this method is rather chaotic. I imagined some variants of this method but they really are work in progress
Iterating through the set to list all k long combinations that sum to x
Pretty self explanatory. This is a naive method that do not work well in my case since I have a pretty large n and a k that is not small enough to avoid a catastrophically big number of combinations (the magnitude of the number of combinations is 10^27!)
I experimented several mechanism related to setting an area of research instead of stupidly iterating through all possibilities, but it's rather complicated and still work in progress
What would you suggest? (Snippets can be in any language, but I prefer C++)
[*] To clear the doubt about whether or not the base collection can contain duplicates, I used the term "array" instead of "set" to be more precise. The collection can contain duplicate integers in my case and quite much, with 70 different integers for 1000 elements (counts rounded), for example
With reasonable sum limit this problem might be solved using extension of dynamic programming approach for subset sum problem or coin change problem with predetermined number of coins. Note that we can count all variants in pseudopolynomial time O(x*n), but output size might grow exponentially, so generation of all variants might be a problem.
Make 3d array, list or vector with outer dimension x-1 for example: A[][][]. Every element A[p] of this list contains list of possible subsets with sum p.
We can walk through all elements (call current element item) of initial "set" (I noticed repeating elements in your example, so it is not true set).
Now scan A[] list from the last entry to the beginning. (This trick helps to avoid repeating usage of the same item).
If A[i - item] contains subsets with size < k, we can add all these subsets to A[i] appending item.
After full scan A[x] will contain subsets of size k and less, having sum x, and we can filter only those of size k
Example of output of my quick-made Delphi program for the next data:
Lst := [1,2,3,3,4,5,6,7];
k := 3;
sum := 10;
3 3 4
2 3 5 //distinct 3's
2 3 5
1 4 5
1 3 6
1 3 6 //distinct 3's
1 2 7
To exclude variants with distinct repeated elements (if needed), we can use non-first occurence only for subsets already containing the first occurence of item (so 3 3 4 will be valid while the second 2 3 5 won't be generated)
I literally translate my Delphi code into C++ (weird, I think :)
int main()
{
vector<vector<vector<int>>> A;
vector<int> Lst = { 1, 2, 3, 3, 4, 5, 6, 7 };
int k = 3;
int sum = 10;
A.push_back({ {0} }); //fictive array to make non-empty variant
for (int i = 0; i < sum; i++)
A.push_back({{}});
for (int item : Lst) {
for (int i = sum; i >= item; i--) {
for (int j = 0; j < A[i - item].size(); j++)
if (A[i - item][j].size() < k + 1 &&
A[i - item][j].size() > 0) {
vector<int> t = A[i - item][j];
t.push_back(item);
A[i].push_back(t); //add new variant including current item
}
}
}
//output needed variants
for (int i = 0; i < A[sum].size(); i++)
if (A[sum][i].size() == k + 1) {
for (int j = 1; j < A[sum][i].size(); j++) //excluding fictive 0
cout << A[sum][i][j] << " ";
cout << endl;
}
}
Here is a complete solution in Python. Translation to C++ is left to the reader.
Like the usual subset sum, generation of the doubly linked summary of the solutions is pseudo-polynomial. It is O(count_values * distinct_sums * depths_of_sums). However actually iterating through them can be exponential. But using generators the way I did avoids using a lot of memory to generate that list, even if it can take a long time to run.
from collections import namedtuple
# This is a doubly linked list.
# (value, tail) will be one group of solutions. (next_answer) is another.
SumPath = namedtuple('SumPath', 'value tail next_answer')
def fixed_sum_paths (array, target, count):
# First find counts of values to handle duplications.
value_repeats = {}
for value in array:
if value in value_repeats:
value_repeats[value] += 1
else:
value_repeats[value] = 1
# paths[depth][x] will be all subsets of size depth that sum to x.
paths = [{} for i in range(count+1)]
# First we add the empty set.
paths[0][0] = SumPath(value=None, tail=None, next_answer=None)
# Now we start adding values to it.
for value, repeats in value_repeats.items():
# Reversed depth avoids seeing paths we will find using this value.
for depth in reversed(range(len(paths))):
for result, path in paths[depth].items():
for i in range(1, repeats+1):
if count < i + depth:
# Do not fill in too deep.
break
result += value
if result in paths[depth+i]:
path = SumPath(
value=value,
tail=path,
next_answer=paths[depth+i][result]
)
else:
path = SumPath(
value=value,
tail=path,
next_answer=None
)
paths[depth+i][result] = path
# Subtle bug fix, a path for value, value
# should not lead to value, other_value because
# we already inserted that first.
path = SumPath(
value=value,
tail=path.tail,
next_answer=None
)
return paths[count][target]
def path_iter(paths):
if paths.value is None:
# We are the tail
yield []
else:
while paths is not None:
value = paths.value
for answer in path_iter(paths.tail):
answer.append(value)
yield answer
paths = paths.next_answer
def fixed_sums (array, target, count):
paths = fixed_sum_paths(array, target, count)
return path_iter(paths)
for path in fixed_sums([1,2,3,3,4,5,6,9], 10, 3):
print(path)
Incidentally for your example, here are the solutions:
[1, 3, 6]
[1, 4, 5]
[2, 3, 5]
[3, 3, 4]
You should first sort the so called array. Secondly, you should determine if the problem is actually solvable, to save time... So what you do is you take the last k elements and see if the sum of those is larger or equal to the x value, if it is smaller, you are done it is not possible to do something like that.... If it is actually equal yes you are also done there is no other permutations.... O(n) feels nice doesn't it?? If it is larger, than you got a lot of work to do..... You need to store all the permutations in an seperate array.... Then you go ahead and replace the smallest of the k numbers with the smallest element in the array.... If this is still larger than x then you do it for the second and third and so on until you get something smaller than x. Once you reach a point where you have the sum smaller than x, you can go ahead and start to increase the value of the last position you stopped at until you hit x.... Once you hit x that is your combination.... Then you can go ahead and get the previous element so if you had 1,1,5, 6 in your thingy, you can go ahead and grab the 1 as well, add it to your smallest element, 5 to get 6, next you check, can you write this number 6 as a combination of two values, you stop once you hit the value.... Then you can repeat for the others as well.... You problem can be solved in O(n!) time in the worst case.... I would not suggest that you 10^27 combinations, meaning you have more than 10^27 elements, mhmmm bad idea do you even have that much space??? That's like 3bits for the header and 8 bits for each integer you would need 9.8765*10^25 terabytes just to store that clossal array, more memory than a supercomputer, you should worry about whether your computer can even store this monster rather than if you can solve the problem, that many combinations even if you find a quadratic solution it would crash your computer, and you know what quadratic is a long way off from O(n!)...
A brute force method using recursion might look like this...
For example, given variables set, x, k, the following pseudo code might work:
setSumStructure find(int[] set, int x, int k, int setIdx)
{
int sz = set.length - setIdx;
if (sz < x) return null;
if (sz == x) check sum of set[setIdx] -> set[set.size] == k. if it does, return the set together with the sum, else return null;
for (int i = setIdx; i < set.size - (k - 1); i++)
filter(find (set, x - set[i], k - 1, i + 1));
return filteredSets;
}
I have a list of lists and I want to process the information inside.
lis = [[1,2,3,4],[1,5,6]]
I want to loop through this list of lists, such that I get 1 *(2*1/1) * (3*2/2) * (4*3/3) and so on. Also, I know that multiplying and dividing by the same number returns the number you started with but I want to implicitly state it in the code. Performing this operation on the list should return
list = [[24],[30]]
I don't know what you are trying to say with the arithmetic you wrote out, but this is probably what you're looking for.
from functools import reduce
lis = [[1,2,3,4],[1,5,6]]
list = [[reduce(lambda x, y: x*y, l)] for l in lis]
In a loop:
list = []
for l in lis:
# do stuff here
list.append(reduce(lambda x, y: x*y, l))
print(list)
output:
[[24], [30]]
See list comprehension, lambda expressions and reduce
NOTE: For those trying to do this in a version before Python 3, I believe reduce is a built-in function.
The multiplication and division by the same number *1/1 either *2/2 in 1 *(2*1/1) * (3*2/2) * (4*3/3) do not make much sense to me.
Using this function you may get the result that you are striving for, i.e. [[24], [30]]
def list_mult(list_in):
list_out = []
for i in list_in:
result = 1
sub_list = []
for j in i:
result = result * j
sub_list.append(result)
list_out.append(sub_list)
print(list_out)
Calling the function with list_mult([[1,2,3,4],[5,6]]) will give [[24], [30]].
So I am trying to add two lists, a nominal list and a random number list, together to create a new list made up of positive values. My issue is that my list of random numbers is being generated from a normal distribution (random.normalvariate(0, SD)) which means occasionally I get negative values when summing the two lists, something I do not want.
I have tried to resolve this issue using a while loop to check whether the sum of the two lists at each item creates a negative value and if it does replace the random number with a new random number. However my code does not seem to be replacing the values no matter how I adjust it. Here is my current attempt.
nominalList = [1,2,3,4,5]
randomList = []
for n in xrange(0, len(nominalList)):
randomList.append(random.normalvariate(0, SD))
while nominalList[n] + randomList[n] < 0:
randomList[n] = random.normalvariate(0, SD)
You need to iterate over the indices again and reassign any that don't meet your criteria:
import random
SD = 7
nominalList = [1,2,3,4,5]
randomList = [random.normalvariate(0, SD) for n in nominalList]
for i,n in enumerate(nominalList):
while n + randomList[i] < 0:
randomList[i] = random.normalvariate(0, SD)
Or another thought, just indent the while in your original code:
nominalList = [1,2,3,4,5]
randomList = []
for n in xrange(0, len(nominalList)):
randomList.append(random.normalvariate(0, SD))
while nominalList[n] + randomList[n] < 0:
randomList[n] = random.normalvariate(0, SD)
So I want to create a subset, run that subset through code, then create a new subset. I'm using a vector for the set and subset. So far I have 3 nested for loops but I'm having trouble figuring out the variables I need.
Here's what I want to do. set = {0, 1, 2, 3, 4, 5} the value matches the index just to simplify this example. I now want subset = {} -> {0} -> {1} -> ... -> {0,1} -> {0,2} -> ... -> {0,5} -> {0,1,2} -> ... -> {0,4,5}. I'm having trouble representing the conditions in terms of variables.
Basically I want the first for loop to increase the subset size. from 0 to set.size() (this is easy). Within that loop, I want to have an iterator corresponding to the index in the element of the subset. I have this iterator initialized to subset.size(), so that we work with the last element first, then work our way to the first element in the subset. then the 3rd for loop, I want to iterate between possible values from the set. Let's say our current subset = {0,1,2} how do I let my program know to put the value '2' inside the last element of the subset, then 1 then 0?
I'm thinking it would involve something with taking the difference from set.size()-1 and subset.size()-1? But I'm not quite sure how. so then I want to iterate through until {0,1,5} and then {0,4,5} but again I'm not sure how to tell the program to stop at 4, as opposed to 5. Again I think this is something with difference but I can't quite figure it out.
to recap:
for loop to iterate through subset size
for loop to iterate through subset "working" element, starting from back
for loop to iterate through that index of subset,
starting from the correct corresponding set value to ending
at the correct corresponding set value
such that the subset goes from {} -> {0} -> {1} ->...-> {0,1} -> {4,5} -> {1,2,3} -> ... -> {1,4,5} and I dont actually need subset = {1,2,3,4,5} but it doesn't hurt my code if I can't stop before that. Again I'm looking to represent the start and end points as variables to make the inner loops work, but I can't figure it out. Huge thanks to anyone who can help me out.
this is approximately how I would go about it.
//handle null subset
for ( int size = 1; size < n; i++ ) {
int indices[size];
for ( int i = 0; i < size; i++ ) indices[i] = i;
while ( indices[0] <= n - size ) {
int i;
for ( i = 1; indices[size - i] == n - i; i-- );
indices[i]++;
for ( i = i + 1; i < size; i++ ) indices[i] = indices[i-1] + 1;
//print out elems using the indices in `indices`
}
//done with all subsets of size `size`
}
The outer loop should be pretty self explanatory. Including 0 seemed like it was going to make some of the inner logic annoying so I started at subsets of size 1.
indices holds the indices of the elements that should be included in the current subset. It starts with the indices 0-size-1.
The condition for the while isn't exactly obvious. The last valid subset this generates contains the last size elements, so if the first index is past n - size we've gone too far.
The inside of the while loop is just incrementing the subset. It looks for the last element that can be incremented and still give a valid subset, increments it, and then resets all of the subsequent elements to be as small as possible. Then you print it out somehow.
And that should be close to something that will do what you want. Let me know if it needs clarifications or corrections.
A trick to enumerate all subsets is to permutate a "selection flag" array, each element of which indicates whether corresponding element in original array is selected.
following is sample code:
void foo(const vector<int>& a)
{
size_t size = a.size();
// selection flag array
// '1' indicates selected, '0' indicates unselected
vector<int> f(size, 0);
for (size_t i = 1; i <= size; i++)
{
// increase the count of selected elements
f[i - 1] = 1;
do
{
for (size_t i = 0; i < size; i++)
{
if (f[i])
{
printf("%d\t", a[i]);
}
}
printf("\n");
} while (next_permutation(f.begin(), f.end(), [](int a, int b){ return a > b; }));
// next_permutation tries to permutate the array
// i.e. '1 1 0 0' -> '1 0 1 0' -> '0 1 1 0' -> ... -> '0 0 1 1'(end)
}
}