Related
I'm wanting to create a list of permutations or cartesian products (not sure which one applies here) where the sum of values in each permutation totals to a provided value.
There should be three parameters required for the function.
Sample Size: The number of items in each permutation
Desired Sum: The total that each permutation should add up to
Set of Numbers: The set of numbers that can be included with repetition in the permutations
I have an implementation working below but it seems quite slow I would prefer to use an iterator to stream the results but I would also need a function that would be able to calculate the total number of items that the iterator would produce.
def buildPerms(sample_size, desired_sum, set_of_number):
blank = [0] * sample_size
return recurseBuildPerms([], blank, set_of_number, desired_sum)
def recurseBuildPerms(perms, blank, values, desired_size, search_index = 0):
for i in range(0, len(values)):
for j in range(search_index, len(blank)):
if(blank[j] == 0):
new_blank = blank.copy()
new_blank[j] = values[i]
remainder = desired_size - sum(new_blank)
new_values = list(filter(lambda x: x <= remainder, values))
if(len(new_values) > 0):
recurseBuildPerms(perms, new_blank, new_values, desired_size, j)
elif(sum(new_blank) <= desired_size):
perms.append( new_blank)
return perms
perms = buildPerms(4, 10, [1,2,3])
print(perms)
## Output
[[1, 3, 3, 3], [2, 2, 3, 3], [2, 3, 2, 3],
[2, 3, 3, 2], [3, 1, 3, 3], [3, 2, 2, 3],
[3, 2, 3, 2], [3, 3, 1, 3], [3, 3, 2, 2],
[3, 3, 3, 1]]
https://www.online-python.com/9cmOev3zlg
Questions:
Can someone help me convert my solution into an iterator?
Is it possible to have a calculation to know the total number of items without seeing the full list?
Here is one way to break this down into two subproblems:
Find all restricted integer partitions of target_sum into sample_size summands s.t. all summands come from set_of_number.
Compute multiset permutations for each partition (takes up most of the time).
Problem 1 can be solved with dynamic programming. I used multiset_permutations from sympy for part 2, although you might be able to get better performance by writing your own numba code.
Here is the code:
from functools import lru_cache
from sympy.utilities.iterables import multiset_permutations
#lru_cache(None)
def restricted_partitions(n, k, *xs):
'partitions of n into k summands using only elements in xs (assumed positive integers)'
if n == k == 0:
# case of unique empty partition
return [[]]
elif n <= 0 or k <= 0 or not xs:
# case where no partition is possible
return []
# general case
result = list()
x = xs[0] # element x we consider including in a partition
i = 0 # number of times x should be included
while True:
i += 1
if i > k or x * i > n:
break
for rest in restricted_partitions(n - x * i, k - i, *xs[1:]):
result.append([x] * i + rest)
result.extend(restricted_partitions(n, k, *xs[1:]))
return result
def buildPerms2(sample_size, desired_sum, set_of_number):
for part in restricted_partitions(desired_sum, sample_size, *set_of_number):
yield from multiset_permutations(part)
# %timeit sum(1 for _ in buildPerms2(8, 16, [1, 2, 3, 4])) # 16 ms
# %timeit sum(1 for _ in buildPerms (8, 16, [1, 2, 3, 4])) # 604 ms
The current solution requires computing all restricted partitions before iteration can begin, but it may still be practical if restricted partitions can be computed quickly. It may be possible to compute partitions iteratively as well, although this may require more work.
On the second question, you can indeed count the number of such permutations without generating them all:
# present in the builtin math library for Python 3.8+
#lru_cache(None)
def binomial(n, k):
if k == 0:
return 1
if n == 0:
return 0
return binomial(n - 1, k) + binomial(n - 1, k - 1)
#lru_cache(None)
def perm_counts(n, k, *xs):
if n == k == 0:
# case of unique empty partition
return 1
elif n <= 0 or k <= 0 or not xs:
# case where no partition is possible
return 0
# general case
result = 0
x = xs[0] # element x we consider including in a partition
i = 0 # number of times x should be included
while True:
i += 1
if i > k or x * i > n:
break
result += binomial(k, i) * perm_counts(n - x * i, k - i, *xs[1:])
result += perm_counts(n, k, *xs[1:])
return result
# assert perm_counts(15, 6, *[1,2,3,4]) == sum(1 for _ in buildPerms2(6, 15, [1,2,3,4])) == 580
# perm_counts(1000, 100, *[1,2,4,8,16,32,64])
# 902366143258890463230784240045750280765827746908124462169947051257879292738672
The function used to count all restricted permutations looks very similar to the function that generates partitions above. The only significant change is in the following line:
result += binomial(k, i) * perm_counts(n - x * i, k - i, *xs[1:])
There are i copies of x to include and k possible positions where x's may end up. To account for this multiplicity, the number of ways to resolve the recursive sub-problem is multiplied by k choose i.
Just started to deal with recursion - I don’t understand everything in it yet. I think that i don't use a basic conditional, but i don't have any idea how to write it. The program itself works and execute everything i need, but there is no recursion.
The idea of the program is that there is a list in which is neede to sum of every x'th number in the list - x here as a step. If x = 0, then the sum is automatically zero. If x is out of range, then the sum is also 0
def sum_elements(nums, x) -> int::
if x not in range(-len(nums), len(nums)) or x == 0:
return 0
if x > 0:
nums = nums[x - 1::x]
return sum(nums)
return sum_elements(nums[::-1], -x)
if __name__ == '__main__':
print(sum_elements([], 0)) # x = 0 -> 0
print(sum_elements([1, 5, 2, 5, 9, 5], 3)) # 2 + 5 = 7
print(sum_elements([5, 6, 10, 20], -2)) # 10 + 5 = 15
print(sum_elements([5, 6, 10, 20], -20)) # x = -20 -> 0
Recursion is when a function calls itself and there a few (non-formal) rules that are always good to keep in the back of your mind when writing these:
1. The base case.
Every recursion function must have a base case that acts as essentially the end of the stack in the recursive call.
2. Every recursive function abides by the non-base(s) and the base case.
In other words, your code must be written in a way that the function either calls itself, or it terminates the recursive call. You can either do this by doing if and else statements, or only writing if statements to catch the base case(s).
3. The input of the function should keep in mind the state of the previous function.
In math, you might remember functions that call themselves (syntax switched for the case of explanation):
f(x)_(n=0) = f(x)_(n=1) + 10
which becomes:
f(x)_(n=1) = ( f(x)_(n=2) + 10 ) + 10
and so on. In essence, you are writing this with code and setting a base case that might say (for the example above, i.e.) "stop when n is 10". If that was the case, you should notice the cascading effect when we are layers deep into that function and when f(x)_(n=10) makes its appearance (and lets says returns 0 + 10) how we would have a final form of f(x)_(n=0) = 0 + 10 + 10 + 10 + ....
So for this function you instead have two inputs, nums and x. These inputs are what we will be modifying as we go down the recursion's stack.
1. Writing our base case.
Writing the base case is typically the easiest part of writing a recursion function. We know, for your problem, the following cases must be caught:
If x is not in the range of the length of nums, then we must return 0.
If len(nums) is 0, then we should return 0.
So lets begin:
def sum_elements(nums, x) -> int:
if len(nums) == 0 or not x in range(-len(nums), len(nums)):
return 0
Notice, however, that range(len([1, 2])) will return range(0, 2) but list(range(0, 2)) will return [0, 1]. Therefore, we must ensure to add a 1 to our len(nums) so that we can truly see if x is within the proper range:
def sum_elements(nums, x) -> int:
if len(nums) == 0 or not x in range(-len(nums), len(nums) + 1):
return 0
Notice that range(-len(nums), len(nums) + 1) for when nums = [1, 2, 3] is equals to range(-3, 4), but list(range(-3, 4)) is equals to [-3, -2, -1, 0, 1, 2, 3]. So therefore, we do not need a -len(nums) + 1 or -len(nums) - 1.
Once we have figured out the base case, we can start working on our actual function. At this point we have done #1 and a portion of #2, but we now must write our non-base(s) case(s).
2. Identifying our other-case(s):
As written in #2, our function input is what is dynamically changing as we go down our function stack. Therefore, we need to think about how we need to modify nums and/or x to fit our purposes. The first thing you should look at, however, is what would happen if we only change one of those variables as we go down the stack.
Keep nums constant, modify x: We know our base case ensures x stays within the constrain of the length of nums in both the positive and negative direction, which is good. However, we must increment x every time the function runs by the original x, or x_0. If we create the function and on every call say x + x, we are not adding the original x to itself, but rather adding the newer x's to itself. This is a problem. Take the following for example:
def sum_elements(nums, x) -> int:
print(nums, x)
# Base case.
if len(nums) == 0 or not x in range(-len(nums), len(nums) + 1):
return 0
# Other case. We must differentiate between positive x, and negative x.
if x > 0:
# Since x is an index that starts at 1, not 0, we must do x-1.
number = nums[x - 1]
else:
# For negative values of x this does not apply. [1, 2][-2] = 1
number = nums[x]
return number + sum_elements(nums, x + x)
Notice how we get:
# [NUMS] x
[1, 2, 3, 4, 5, 6] 2
[1, 2, 3, 4, 5, 6] 4
[1, 2, 3, 4, 5, 6] 8
# OUTPUT
6
and how the x value on the third call is 8. This is no bueno. The more you practice recursion, the more intuitive this concept will become on noticing how changing a certain input might not be the best. You ought to think: "what will this value be when the function continues down the stack?"
Keep x constant, modify nums: If we do this way we should be certain that we will not have issues with the value of x. The issue, then, becomes how we will modify the nums list and use x for our advantage. What we do know, is that x can be technically used as an index, as demonstrated above. So, therefore, what if instead of modifying the index, we modify the list in which that index takes from? Take the following for example:
nums = [1, 2, 3, 4]
x = 2
print(nums) # > [1, 2, 3, 4]
print(nums[x - 1]) # > 2
nums = nums[x:] # > [3, 4]
print(nums[x - 1]) # > 4
So it does seem like we can modify the list and keep a constant x to retrieve the information we want. Awesome! In such case #2 is the way to go.
3. Writing our other-case(s).
So now we will try to now write a function that keeps x constant, but modifies nums. We have a general idea from the code above, and we know from the previous point that we will have to deal with -x and x differently. Therefore, lets write something:
def sum_elements2(nums, x) -> int:
# Base case.
if len(nums) == 0 or not x in range(-len(nums), len(nums) + 1):
return 0
# Other case.
if x >= 0:
number = nums[x - 1]
nums = nums[x:]
else:
number = nums[x]
# Not sure what goes here.
return number + sum_elements(nums, x)
If we test the function above, it seems that it works for any positive x, but won't work for negative values of x. It makes sense, however, that whatever we do to the positive side, we must do the opposite to the negative side. If we try to use nums = nums[:x] we very quickly realize it works. Our final function becomes:
def sum_elements(nums, x) -> int:
# Base case.
if len(nums) == 0 or not x in range(-len(nums), len(nums) + 1):
return 0
# Other case.
if x >= 0:
number = nums[x - 1]
nums = nums[x:]
else:
number = nums[x]
nums = nums[:x]
return number + sum_elements(nums, x)
Running Examples
If we run examples with the above function, we get:
print(sum_elements([1, 2, 3, 4, 5, 6], 2)) # > 2 + 4 + 6 = 12
print(sum_elements([], 0)) # > 0
print(sum_elements([1, 5, 2, 5, 9, 5], 3)) # > 7
print(sum_elements([5, 6, 10, 20], -2)) # > 15
print(sum_elements([5, 6, 10, 20], -20)) # > 0
Maybe this approach can help you understand.
It starts from the first element and sums the rest every x ones.
That is my assumption, as you haven't provided an input and its desired output as an example.
In case you need to start from the xth element the code can be easily modified, I leave it to you to experiment with it.
def sum_elements(nums, x) -> int:
if x>0 and x<=len(nums):
return nums[0] + sum_elements(nums[x:], x)
return 0
lst = [1, 2, 3, 4, 5, 6]
print(sum_elements(lst, 2))
print(sum_elements(lst, 3))
print(sum_elements(lst, 0))
produces
9
5
0
Note: it just demonstrates recursion, but it's not optimal for a number of reasons.
Also it discards negative values of x
I am having an issue where I have been given an array of numbers [1,3,5] and need to find the least amount of numbers that could add to a specific number. Each number has a weight and I need to calculate the most efficient. For example if the number was 6 I would need to use [5,1] instead of [3,3] as 5 has a greater importance. In the case of 12 it would be [5,5,1,1] instead of [3,3,3,3]
I have already tried implementing dictionaries and arrays but the problem solving part is what I am having trouble with.
A valid way to do it, not relying on the presence of 1 in the list, is to try to use as many of the largest numbers as possible, and recursively try to obtain the remainder:
If no solution can be found, the function will return None
def solve(numbers, target):
'''Return a list of the largest of numbers whose sum is target,
None if impossible'''
if not numbers:
return None
# make sure that numbers is sorted
numbers = list(sorted(numbers))
# get the largest number and remove it from the list
largest = numbers.pop()
# we start with as many times the largest number as possible
quotient, remainder = divmod(target, largest)
# did we reach the target?
if remainder == 0:
return [largest] * quotient
# if not, try with a deacreasing number of times the largest
# (including 0 times)
for n in range(quotient, -1, -1):
remainder = target - n * largest
# and recursively try to obtain the remainder with the remaining numbers
solution = solve(numbers, remainder)
if solution:
return [largest] * n + solution
else:
return None
Some tests:
solve([1, 3, 5], 12)
# [5, 5, 1, 1]
solve([3, 5], 12) # no 1, we have to use smaller numbers
# [3, 3, 3, 3]
solve([7, 3, 4], 15)
# [7, 4, 4]
solve([3, 4], 5) # Impossible
# None
Keep looping, until n = 0, by taking away the largest number, then smaller numbers if n < 0.
As pointed out by Thierry Lathuille, This will probably not work if there is no 1 in your array. If that is the case, you might want to fiddle with the if n < 0 lines.
n = int(input())
a = [1, 3, 5]
ans = []
while n > 0:
n -= max(a)
if n == 0:
ans.append(max(a))
break
if n > 0:
ans.append(max(a))
if n < 0:
n += max(a)
a.pop(a.index(max(a)))
print(ans)
For instance, given list [1, 0, 1] the code would return [1,1,0]. Other examples:
[1,1,1] -- > [1,0,0,0]
[1,0,0,1] --> [1,0,1,0]
I'm having most trouble understanding what my base case for recursion would be and then how to implement for the (n-1) case.
def increment_helper(number):
newNum = []
if len(number) ==1:
if number[0] == 1:
carry = 1
newNum.append(0)
else:
carry = 0
newNum.append(1)
else:
return increment_helper(number-1)
return newNum
So I'm sure that there are a lot of errors in here specifically how I am calling my recursion because I am not sure how to recurse on the list while storing the number that is removed somehow. The else return statement is obviously incorrect but I am using that as a placeholder. I am unsure of what condition to use as my base case for incrementation. I think I should be using a carry variable that keeps track of whether I am carrying a one over but other than that I am stuck on how to proceed.
Ah, ha! Okay, you have some idea of what you're doing. The basic outline is
Base case: how do I know when I'm done?
You're done when you run out of digits. number should be a list of individual digits; check its length to figure out when not to recur.
Recursion case: what next?
The general concept of recursion is "do something simple, reduce the problem by a small amount, and recur with the smaller problem." Your job in this part is to do the addition for one digit. If you need to keep going (is there a carry from that digit?), then recur. Otherwise, you have all the info you need to finish.
Specific application
Your recursion step will involve calling increment_helper with one digit less: not number - 1, but number[:-1].
After you return from each recursion, you'lll then want to append the digit you just finished. For instance, if you're incrementing 1101, your first call will see that the right-hand one, incremented, has a carry. The new digit is 0, and you have to recur. Hold onto the 0 for a moment, call yourself with 110, and get the result of that call. Append your saved 0 to that, and return to your main program.
Does that get you moving?
This one is "tricky" because you have two things going at each step:
what is the current number I'm looking at? (binary question 0/1)
should it be incremented? (do we have carry?) (binary question yes/no)
This leads to 4 cases.
There is the extra "case" of did we even get a list but it isn't that interesting.
So I would describe it as follows:
if not carry:
# technically meaningless so just return the entire list immediately
return list
# we have "carry", things will change
if not list:
# assumes [] == [0]
return [1]
if list[0]:
# step on `1` (make it 0 and carry)
return [0] + increment(rest_of_list)
# step on `0` (make it 1 and no more carry)
return [1] + rest_of_list
I strongly advise to change lists to tuples and work with these.
Also note that the recursion is on the reversed list, so apply as follows:
def increment_helper(lst, carry):
if not carry:
return lst
if not lst:
return [1]
if lst[0]:
return [0] + increment_helper(lst[1:], True)
return [1] + lst[1:]
def increment(lst):
# long but really just reverse input and output
return increment_helper(lst[::-1], True)[::-1]
I used some shortcuts by returning lists immediately (short-circuiting), but you can make it more "pure" by carrying on the recursion even without carry.
Another recursive approach.
Is the current input an empty list?
If yes return [1]
If no, continue
Is the sum (value) of the last element in the list and 1 greater than 1?
If so recursively call your function on the list without the last element (number_list[:-1]) and append [0] to the result.
If no, set the last element of the list to the sum.
Return the number_list
Code:
def increment_helper(number_list):
if not number_list:
return [1]
value = number_list[-1] + 1
if value > 1:
number_list = increment_helper(number_list[:-1]) + [0]
else:
number_list[-1] = value
return number_list
Example output:
numbers = [[1, 0, 1], [1,1,1], [1,0,0,1]]
for n in numbers:
print("%r ---> %r" % (n, increment_helper(n)))
#[1, 0, 1] ---> [1, 1, 0]
#[1, 1, 1] ---> [1, 0, 0, 0]
#[1, 0, 0, 1] ---> [1, 0, 1, 0]
Try this:
def add1(listNum):
if listNum.count(0):
oneArr = [[0] * (len(listNum) - 1)] + [1]
sumArr = []
for i in range(len(listNum)):
sumArr.append(sum(listNum[i], oneArr[i]))
newArr = []
for j in range(len(sumArr) - 1):
if sumArr[len(sumArr) - 1 - j] < 2:
newArr.insert(0, sumArr[len(sumArr) - 1 - j])
else:
newArr.insert(0, 1)
sumArr[len(sumArr) - 1 - j] += 1
return sumArr
else:
return [1] + [[0] * len(listNum)]
There aren't many reasons for using recursion for a program as simple as this, which is why I have chosen to provide a non-recursive solution.
In case it interests you, I've calculated the time complexity of this function and it is O(n).
It may be best to use two functions: one to check if a simple increment of the last position would suffice and another to perform the recursion should the previous attempt fail:
vals = [[1, 0, 1], [1,1,1], [1,0,0,1]]
def update_full(d):
if all(i in [1, 0] for i in d):
return d
start = [i for i, a in enumerate(d) if a > 1]
return update_full([1]+[0 if i > 1 else i for i in d] if not start[0] else [a+1 if i == start[0] -1 else 0 if i == start[0] else a for i, a in enumerate(d)])
def increment(d):
if not d[-1]:
return d[:-1]+[1]
return update_full(d[:-1]+[2])
print(list(map(increment, vals)))
Output:
[[1, 1, 0], [1, 0, 0, 0], [1, 0, 1, 0]]
You can treat the (binary) digits recursively by traversing the number from tail to head (just like addition works).
Before performing the recursion you have to check for two special cases:
No increment at the current digit is to be performed. Then just return the unmodified digits.
A single digit remains (you're at the head of the number). Then you possibly need to append the overflow to the head.
For the remaining cases you can increment the current digit and treat all digits before the current one in a recursive manner.
def bin_incr(n, incr=True):
if not incr:
return n
if len(n) == 1:
return [1, 0] if n[0] == 1 else [1]
return (
# `n[-1] == 1` denotes an overflow to the next digit.
bin_incr(n[:-1], n[-1] == 1)
+ [(n[-1] + 1) % 2]
)
I understand you don't want to use decimal addition, but if you must use big endian bit order, converting back to decimal first is probably the most practical. Otherwise, you end up with unnecessary reverse calls or awkward negative array indices
def binary (dec): # big endian bit order
if dec < 2:
return [ dec ]
else:
return binary (dec >> 1) + [ dec & 1 ]
def decimal (bin, acc = 0):
if not bin:
return acc
else:
return decimal (bin[1:], (acc << 1) + bin[0])
def increment (bin):
# sneaky cheat
return binary (decimal (bin) + 1)
for x in range (10):
print (x, binary (x), increment (binary (x)))
# 0 [0] [1]
# 1 [1] [1, 0]
# 2 [1, 0] [1, 1]
# 3 [1, 1] [1, 0, 0]
# 4 [1, 0, 0] [1, 0, 1]
# 5 [1, 0, 1] [1, 1, 0]
# 6 [1, 1, 0] [1, 1, 1]
# 7 [1, 1, 1] [1, 0, 0, 0]
# 8 [1, 0, 0, 0] [1, 0, 0, 1]
# 9 [1, 0, 0, 1] [1, 0, 1, 0]
If however you can represent your binary numbers in little endian bit order, things change. Instead of converting back to decimal, increment can be defined directly as a beautiful recursive function
def binary (dec): # little endian bit order
if dec < 2:
return [ dec ]
else:
return [ dec & 1 ] + binary (dec >> 1)
def increment (bin):
if not bin:
return [1]
elif bin[0] == 0:
return [1] + bin[1:]
else:
return [0] + increment(bin[1:])
for x in range (10):
print (x, binary (x), increment (binary (x)))
# 0 [0] [1]
# 1 [1] [0, 1]
# 2 [0, 1] [1, 1]
# 3 [1, 1] [0, 0, 1]
# 4 [0, 0, 1] [1, 0, 1]
# 5 [1, 0, 1] [0, 1, 1]
# 6 [0, 1, 1] [1, 1, 1]
# 7 [1, 1, 1] [0, 0, 0, 1]
# 8 [0, 0, 0, 1] [1, 0, 0, 1]
# 9 [1, 0, 0, 1] [0, 1, 0, 1]
Aside: converting the little endian representation back to decimal is a little different. I provide this to show that use cases for recursion exist everywhere
def decimal (bin, power = 0):
if not bin:
return 0
else:
return (bin[0] << power) + decimal (bin[1:], power + 1)
This part of the answer gives you cake and allows you to eat it too. You get big endian bit order and a recursive increment that steps through the bits in left-to-right order – You should use either implementation above for a number of reasons, but this aims to show you that even though your problem is complex, it's still possible to think about it recursively. No reverse or arr[::-1] was misused in the making of this function.
def binary (dec): # big endian bit order
if dec < 2:
return [ dec ]
else:
return binary (dec >> 1) + [ dec & 1 ]
def increment (bin, cont = lambda b, carry: [1] + b if carry else b):
if bin == [0]:
return cont ([1], 0)
elif bin == [1]:
return cont ([0], 1)
else:
n, *rest = bin
return increment (rest, lambda b, carry:
cont ([n ^ carry] + b, n & carry))
for x in range (10):
print (x, binary (x), increment (binary (x)))
# 0 [0] [1]
# 1 [1] [1, 0]
# 2 [1, 0] [1, 1]
# 3 [1, 1] [1, 0, 0]
# 4 [1, 0, 0] [1, 0, 1]
# 5 [1, 0, 1] [1, 1, 0]
# 6 [1, 1, 0] [1, 1, 1]
# 7 [1, 1, 1] [1, 0, 0, 0]
# 8 [1, 0, 0, 0] [1, 0, 0, 1]
# 9 [1, 0, 0, 1] [1, 0, 1, 0]
We start by breaking the problem up into smaller parts; n is the first problem, and rest is the rest of the problems. But the key to thinking with continuations (like cont above) is to think big.
In this particular problem, n gets updated based on whether rest gets updated. So we immediately recur on rest and pass a continuation that will receive the result of the subproblem. Our continuation receives the answer to the subproblem b, and whether or not that subproblem results in a carry.
...
else:
n, *rest = bin
return increment (rest, lambda b, carry:
cont ([n ^ carry] + b, n & carry))
The n ^ carry and n & carry expressions determine what the answer to this subproblem is and what the next carry will be. The following truth table shows that ^ and & encodes our answer and next_carry respectively. For example, if n is 0 and carry is 1, the carry can be consumed. The answer will be [1] + the answer to the subproblem and the next carry will be 0.
n carry (answer, next_carry) n ^ carry n & carry
0 0 ([0] + b, 0) 0 0
0 1 ([1] + b, 0) 1 0
1 0 ([1] + b, 0) 1 0
1 1 ([0] + b, 1) 0 1
The base cases are simple. If the subproblem is [0], the answer is [1] and no carry of 0. If the subproblem is [1], then the answer is [0]with a carry of 1
...
if bin == [0]:
return cont ([1], 0)
elif bin == [1]:
return cont ([0], 1)
Lastly, design the default continuation – if the answer to the problem b results in a carry, simply prepend [1] to the answer, otherwise just return the answer.
cont = lambda b, carry: [1] + b if carry else b
You are asking for the increment/successor/next function that will generate a sequence of sequences. Since other have given code, I will give a general method for developing such functions.
First, develop a multiple recursion (2 or more recursive calls) for calculating, say, all sequences of the type of length N. For binary sequences (bs) in big-endian order, from N 0s to N 1s, the base case bs(0) expression is [[]], the sequence that contains the one sequence with no binary digits. The double recursion for bs(n) in terms of bs(n-1) is ([0] concatenated to all members of bs(n-1) (in order)) plus ([1] contanenated to all members of bs(n-1)).
Next, focus on the transition between the subsequences returned by adjacent recursive calls. Here there is just one: 0, 1, ..., 1 to 1, 0, ..., 0. To increment across this boundary, we need to replace 0 followed by 0 or more 1s by 1 followed by the same number of 0s. We find such breaks by scanning from the right for the first 0, as others have shown.
It turns out the every increment crosses the boundary between adjacent bs(k) calls for some value of k, which is to say, at some level of the tree of calls resulting from double recursion.
So far, that I know of, the same idea works for designing the increment function for sequences of grey codes, sequences of conbinations (n things taken k at a time), and sequences of permutations.
Note 1: the 1->0 transitions can be done 1 at a time or all at once.
Note 2: the binary bit testing and flipping is the turing machine algorithm for count + 1. Stephen Wolfram, A New Kind of Science, presents, as I remember, 3 different implementations in the TM chapter.
Note 3 (added in edit): If one switches from prepending 0 first to prepending 1 first, or from prepending to appending, or both, one gets 3 other sequence of sequences, with 3 other increment functions.
You do not need recursion in this case. Let us start with the simplest implementation:
implement a full adder which will operate on bits.
implement a ripple adder using the full adder which will operate on 2 lists of bits.
The full adder implementation is straight forrward.
def full_adder(a,b,cin):
sum_ = a^(b^cin)
cout = (a&b) | (a|b)&cin
return sum_, cout
Tests to make sure the full adder conforms to the specs:
>>> zero_one = (0,1)
>>> [full_adder(*x) for x in [(a,b,c) for a in zero_one for b in zero_one for c in zero_one]]
[(0, 0), (1, 0), (1, 0), (0, 1), (1, 0), (0, 1), (0, 1), (1, 1)]
Since the parameters of the ripple adder are lists, we need to ensure the list lengths match before the addition. This is done by padding the shorter list with leading zeros.
def ripple_adder(xs,ys):
x, y = map(len, (xs, ys))
alen = max(x, y)
ax, by = map(lambda f: f if len(f) == alen else [0]*(alen-len(f)) + f, (xs, ys))
cout = 0
res = [0]*(alen)
for i in range(alen-1, -1, -1):
a, b, cin = ax[i], by[i], cout
s, cout = full_adder(a, b, cin)
res[i] = s
if cout:
res = [1] + res
return res
Finally, we define bin_inc the binary increment function in terms of the ripple adder
def bin_inc(bin_lst):
return ripple_adder(bin_lst, [1])
>>> bin_inc([1,1,1])
[1, 0, 0, 0]
>>> bin_inc([1,0,0,1])
[1, 0, 1, 0]
Now for a solution that is simpler but requires a little insight. Consider the following
obervations for an input xs with length L and output ys
if xs is all ones, ys will have length L+1, the first element of ys will be 1 and the rest will be zeros.
if xs has a zero element, let p be the index of the last zero element in xs.
Then ys will be the list consisting of the first p elements of xs followed by a 1 followed by zeros of length L - p. That is
ys = xs[:p] + [1] + [0]*(L-p).
We can translate this to python code easily. We use python's list.index method to find the last occurence of zero by working on the reverse of the list and adjust the algorithm appropriately:
def bin_inc_2(xs):
if all(xs):
return [1] + [0]*len(xs)
p = xs[::-1].index(0)
return xs[:-p-1] + [1] + [0]*p
This works and is simpler than the ripple_adder based implementation. One minor drawback you might notice is when xs has a zero element, we traverse it to check if it is all ones, then traverse it again to find the first occurence of zero.
We can simplify the implementation and make it more pythonic atthe same time by using a try except block:
def bin_inc_3(bin_lst):
try:
p = bin_lst[::-1].index(0)
return bin_lst[:-p-1] + [1] + [0]*p
except ValueError:
return [1] + [0]*len(bin_lst)
This implementation is simple in terms of source text, and idiomatic python. Now we test it against the ripple_adder based adder to make sure it works well.
>>> z_n = (0,1)
>>> xs = [[a,b,c,d,e,f,g,h] for a in z_n for b in z_n for c in z_n for d in z_n
for e in z_n for f in z_n for g in z_n for h in z_n ]
>>> print(all(ripple_adder(x, [1]) == bin_inc_3(x) for x in xs))
True
Fantastic, it works as intended and correctly handles leading zeros as evidenced by the tests (which increments every number from 0 to 255).
There's only a need to recurse when there is a carry:
def f(n):
# Base cases
if not n:
return [1]
if n == [1]:
return [1, 0]
if n[-1] == 0:
return n[:-1] + [1]
if n[-2:] == [0, 1]:
return n[:-2] + [1, 0]
# Recurse
return f(n[:-2]) + [0, 0]
numbers = [[1, 0, 1], [1,1,1], [1,0,0,1], [1, 0, 1, 1]]
for n in numbers:
print n, f(n)
I'm trying to get n random and non-overlapping slices of a sequence where each subsequence is of length l, preferably in the order they appear.
This is the code I have so far and it's gotten more and more messy with each attempt to make it work, needless to say it doesn't work.
def rand_parts(seq, n, l):
"""
return n random non-overlapping partitions each of length l.
If n * l > len(seq) raise error.
"""
if n * l > len(seq):
raise Exception('length of seq too short for given n, l arguments')
if not isinstance(seq, list):
seq = list(seq)
gaps = [0] * (n + 1)
for g in xrange(len(seq) - (n * l)):
gaps[random.randint(0, len(gaps) - 1)] += 1
result = []
for i, g in enumerate(gaps):
x = g + (i * l)
result.append(seq[x:x+l])
if i < len(gaps) - 1:
gaps[i] += x
return result
For example if we say rand_parts([1, 2, 3, 4, 5, 6], 2, 2) there are 6 possible results that it could return from the following diagram:
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
[1, 2, 3, 4, 5, 6]
____ ____
So [[3, 4], [5, 6]] would be acceptable but [[3, 4], [4, 5]] wouldn't because it's overlapping and [[2, 4], [5, 6]] also wouldn't because [2, 4] isn't contiguous.
I encountered this problem while doing a little code golfing so for interests sake it would also be nice to see both a simple solution and/or an efficient one, not so much interested in my existing code.
def rand_parts(seq, n, l):
indices = xrange(len(seq) - (l - 1) * n)
result = []
offset = 0
for i in sorted(random.sample(indices, n)):
i += offset
result.append(seq[i:i+l])
offset += l - 1
return result
To understand this, first consider the case l == 1. Then it's basically just returning a random.sample() of the input data in sorted order; in this case the offset variable is always 0.
The case where l > 1 is an extension of the previous case. We use random.sample() to pick up positions, but maintain an offset to shift successive results: in this way, we make sure that they are non-overlapping ranges --- i.e. they start at a distance of at least l of each other, rather than 1.
Many solutions can be hacked for this problem, but one has to be careful if the sequences are to be strictly random. For example, it's wrong to begin by picking a random number between 0 and len(seq)-n*l and say that the first sequence will start there, then work recursively.
The problem is equivalent to selecting randomly n+1 integer numbers such that their sum is equal to len(seq)-l*n. (These numbers will be the "gaps" between your sequences.) To solve it, you can see this question.
This worked for me in Python 3.3.2. It should be backwards compatible with Python 2.7.
from random import randint as r
def greater_than(n, lis, l):
for element in lis:
if n < element + l:
return False
return True
def rand_parts(seq, n, l):
"""
return n random non-overlapping partitions each of length l.
If n * l > len(seq) raise error.
"""
if n * l > len(seq):
raise(Exception('length of seq too short for given n, l arguments'))
if not isinstance(seq, list):
seq = list(seq)
# Setup
left_to_do = n
tried = []
result = []
# The main loop
while left_to_do > 0:
while True:
index = r(0, len(seq) - 1)
if greater_than(index, tried, l) and index <= len(seq) - left_to_do * l:
tried.append(index)
break
left_to_do -= 1
result.append(seq[index:index+l])
# Done
return result
a = [1, 2, 3, 4, 5, 6]
print(rand_parts(a, 3, 2))
The above code will always print [[1, 2], [3, 4], [5, 6]]
If you do it recursively it's much simpler. Take the first part from (so the rest will fit):
[0:total_len - (numer_of_parts - 1) * (len_of_parts)]
and then recurse with what left to do:
rand_parts(seq - begining _to_end_of_part_you_grabbed, n - 1, l)
First of all, I think you need to clarify what you mean by the term random.
How can you generate a truly random list of sub-sequences when you are placing specific restrictions on the sub-sequences themselves?
As far as I know, the best "randomness" anyone can achieve in this context is generating all lists of sub-sequences that satisfy your criteria, and selecting from the pool however many you need in a random fashion.
Now based on my experience from an algorithms class that I've taken a few years ago, your problem seems to be a typical example which could be solved using a greedy algorithm making these big (but likely?) assumptions about what you were actually asking in the first place:
What you actually meant by random is not that a list of sub-sequence should be generated randomly (which is kind of contradictory as I said before), but that any of the solutions that could be produced is just as valid as the rest (e.g. any of the 6 solutions is valid from input [1,2,3,4,5,6] and you don't care which one)
Restating the above, you just want any one of the possible solutions that could be generated, and you want an algorithm that can output one of these valid answers.
Assuming the above here is a greedy algorithm which generates one of the possible lists of sub-sequences in linear time (excluding sorting, which is O(n*log(n))):
def subseq(seq, count, length):
s = sorted(list(set(seq)))
result = []
subseq = []
for n in s:
if len(subseq) == length:
result.append(subseq)
if len(result) == count:
return result
subseq = [n]
elif len(subseq) == 0:
subseq.append(n)
elif subseq[-1] + 1 == n:
subseq.append(n)
elif subseq[-1] + 1 < n:
subseq = [n]
print("Impossible!")
The gist of the algorithm is as follows:
One of your requirements is that there cannot be any overlaps, and this ultimately implies you need to deal with unique numbers and unique numbers only. So I use the set() operation to get rid all the duplicates. Then I sort it.
Rest is pretty straight forward imo. I just iterate over the sorted list and form sub-sequences greedily.
If the algorithm can't form enough number of sub-sequences then print "Impossible!"
Hope this was what you were looking for.
EDIT: For some reason I wrongly assumed that there couldn't be repeating values in a sub-sequence, this one allows it.
def subseq2(seq, count, length):
s = sorted(seq)
result = []
subseq = []
for n in s:
if len(subseq) == length:
result.append(subseq)
if len(result) == count:
return result
subseq = [n]
elif len(subseq) == 0:
subseq.append(n)
elif subseq[-1] + 1 == n or subseq[-1] == n:
subseq.append(n)
elif subseq[-1] + 1 < n:
subseq = [n]
print("Impossible!")