Carry numbers in list - python-3.x

I am making a code guessing program that asks for an input of an example key code, then the program tries combinations of characters and finds how long it took.
What I need to do is have a function that takes in a list of numbers, and returns a list of numbers, the hard thing being that the length could be anything. I currently have this:
def increment(nums):
nums[len(nums) - 1] += 1
for i in range(len(nums)):
if nums[-i + 1] == 62:
nums[-i + 1] = 0
nums[-i] += 1
if nums[0] == 62:
return [0 for n in nums]
return nums
I am trying to make the last index increase, when that reaches 62, increment the second to last by 1, then set the last to 0, continuing on and on. Then, when the first number reaches 62, it returns a list of zeros.
The expected output is as follows:
[0,0,0,1]
[0,0,0,2]
[0,0,0,3]
[0,0,0,4]
...
[0,0,0,61]
[0,0,1,0]
...
The current output is as follows:
[0,0,0,1]
[0,0,0,2]
[0,0,0,3]
[0,0,0,4]
...
[0,62,0,0]
At this point I am confused. Is there anything I am doing wrong?

Your indices in the carry loop are off, you're adding 1 and nothing, when you want to subtract 1 and 2:
def increment(nums):
nums[-1] += 1 # len(nums) - 1 is a slow verbose way to index at -1 in Python
for i in range(len(nums)):
if nums[-i - 1] == 62: # Change + 1 to - 1
nums[-i - 1] = 0 # Change + 1 to - 1
nums[-i - 2] += 1 # Change no adjustment to -2
if nums[0] == 62:
nums[:] = [0] * len(nums) # Always mutate argument to match return
return nums
This will still fail when you hit the wraparound case (due to an index out of bounds issue on the increment), and it involves more small math operations than needed, so we can improve it a bit and fix the bug by adjusting the range to run one fewer times, and remove two of the index fixups:
# Run one fewer times, iterate as negative numbers directly, and start
# from -1, not 0 so no adjustment needed for two of three lookups
for i in range(-1, -len(nums), -1):
if nums[i] == 62:
nums[i] = 0
nums[i - 1] += 1
If speed was important, you could get a bit more clever with enumerate so you're usually not even doing indexing, but this is close to what you already had, and premature optimization is the root of all evil. :-)
Note that if the goal is just to make an iterator that produces lists of this form sequentially, as I mention in the comments, the itertools module provides simpler ways to do this. For memory reasons, we can't use itertools.cycle (it would end up eventually storing the entire run of values, about 15 million of them for the four element case), but we can simulate it using chain:
from itertools import chain, product, repeat
carrygen = chain.from_iterable(product(range(62), repeat=4) for _ in repeat(None))
next(carrygen) # To skip all zero entry the first time around
carrygen = map(list, carrygen) # Returns lists instead of tuples
for nums in carrygen:
print(nums)
which would output indefinitely starting with until you break the loop or kill the process:
[0,0,0,1]
[0,0,0,2]
[0,0,0,3]
[0,0,0,4]
...
[0,0,0,61]
[0,0,1,0]
...

I don't think your list indices are working like you expect in your for loop, but rather than try to unravel that, I'd suggest that this would be a good use for a recursive function:
def increment(nums):
# Condition for ending the recursion when nums is empty
if not nums:
return []
# Check if we're overflowing the last item...
if nums[-1] < 61:
# ...if not, just increment it and return
nums[-1] += 1
return nums
else:
# ...if so, strip it off, increment the rest of the list, and
# tack on a 0 to the end
return increment(nums[:-1]) + [0]

Related

Number of sub sequences of length K having total sum S, given 2d array

I wish to find Number of sub sequences of length K having total sum S, given an array.
Sample Input:
a=[1,1,1,2,2] & K=2 & S=2
Sample Output:
3 {because a[0],a[1]; a[1]a[2]; a[0]a[2] are only three possible for the case}
I have tried to write a recursive loop in Python for starter but it isn't giving output as expected.Please can you help me find a loophole I might be missing on.
def rec(k, sum1, arr, i=0):
#print('k: '+str(k)+' '+'sum1: '+str(sum1)) #(1) BaseCase:
if(sum1==0 and k!=0): # Both sum(sum1) required and
return 0 # numbers from which sum is required(k)
if(k==0 and sum1 !=0): # should be simultaneously zero
return 0 # Then required subsequences are 1
if(k==0 and sum1==0 ): #
return 1 #
base_check = sum1!=0 or k!=0 #(2) if iterator i reaches final element
if(i==len(arr) and base_check): # in array we should return 0 if both k
return 0 # and sum1 aren't zero
# func rec for getting sum1 from k elements
if(sum1<arr[0]): # takes either first element or rejects it
ans=rec(k-1,sum1,arr[i+1:len(arr)],i+1) # so 2 cases in else loop
print(ans) # i is taken in as iterator to provide array
else: # input to rec func from 2nd element of array
ans=rec(k-1, sum1-arr[0], arr[i+1:len(arr)],i+1)+rec(k, sum1, arr[i+1:len(arr)],i+1)
#print('i: '+str(i)+' ans: '+str(ans))
return(ans)
a=[1,1,1,2,2]
print(rec(2,2,a))
I am still unable to process how to make changes. Once this normal recursive code is written I might go to DP approach accordinlgy.
Using itertools.combinations
Function itertools.combinations returns all the subsequences of a given lengths. Then we filter to keep only subsequences who sum up to the desired value.
import itertools
def countsubsum(a, k, s):
return sum(1 for c in itertools.combinations(a,k) if sum(c)==s)
Fixing your code
Your code looks pretty good, but there are two things that appear wrong about it.
What is this if for?
At first I was a bit confused about if(sum1<arr[0]):. I think you can (and should) always go to the else branch. After thinking about it some more, I understand you are trying to get rid of one of the two recursive calls if arr[0] is too large to be taken, which is smart, but this makes the assumption that all elements in the array are nonnegative. If the array is allowed to contain negative numbers, then you can include a large a[0] in the subsequence, and hope for a negative element to compensate. So if the array can contain negative numbers, you should get rid of this if/else and always execute the two recursive calls from the else branch.
You are slicing wrong
You maintain a variable i to remember where to start in the array; but you also slice the array. Pretty soon your indices become wrong. You should use slices, or use an index i, but not both.
# WRONG
ans=rec(k-1, sum1-arr[0], arr[i+1:len(arr)],i+1)+rec(k, sum1, arr[i+1:len(arr)],i+1)
# CORRECT
ans = rec(k-1, sum1-arr[i], arr, i+1) + rec(k, sum1, arr, i+1)
# CORRECT
ans = rec(k-1, sum1-arr[0], arr[1:]) + rec(k, sum1, arr[1:])
To understand why using both slicing and an index gives wrong results, run the following code:
def iter_array_wrong(a, i=0):
if (a):
print(i, a)
iter_array_wrong(a[i:], i+1)
def iter_array_index(a, i=0):
if i < len(a):
print(i, a)
iter_array_index(a, i+1)
def iter_array_slice(a):
if a:
print(a)
iter_array_slice(a[1:])
print('WRONG')
iter_array_wrong(list(range(10)))
print()
print('INDEX')
iter_array_index(list(range(10)))
print()
print('SLICE')
iter_array_slice(list(range(10)))
Also note that a[i:len(a)] is exactly equivalent to a[i:] and a[0:j] is equivalent to a[:j].
Clean version of the recursion
Recursively count the subsequences who use the first element of the array, and the subsequences who don't use the first element of the array, and add the two counts. To avoid explicitly slicing the array repeatedly, which is an expensive operation, we keep a variable start to remember we are only working on subarray a[start:].
def countsubsum(a, k, s, start=0):
if k == 0:
return (1 if s == 0 else 0)
elif start == len(a):
return 0
else:
using_first_element = countsubsum(a, k-1, s-a[start], start+1)
notusing_first_elem = countsubsum(a, k, s, start+1)
return using_first_element + notusing_first_elem

Array sorting timing out for huge size of arrays

I am doing an online code challenge. I have an array which I need to sort and record to minimum number of iterations required to be sorted. I have the following code.
def minSwap(ar):
c = 0
for i in range(0, len(ar)):
if ar[i] == i+1:
continue
else:
for k in range(i+1, len(ar)):
if ar[k] == i+1:
ar[k] = ar[i]
ar[i] = i+1
c = c+1
break
return c
This code passes majority of test cases, however for really huge number of case such as where array size is beyond (let's say 50000) it gets timeout.
How can I identify the faulty block of code? I can't see a way to tweak it further.
Looking at the problem statement, it looks like you want to sort a list that has numbers starting from 1 thru n.
If you are trying to sort the list, and the final list is expected to be [1, 2, 3, 4, 5, 6, 7 , 8, .....], then all you need to do is to insert the i+1 to the current position and pop the value of i+1 from its current position. That will reduce the number of iterations you need to sort or swap.
Here's the code that does this with least number of moves. At least that's what I found based on my tests.
def minSwap(ar):
c=0
for i in range(0, len(ar)):
if ar[i] != i+1:
#find the value of i+1 from i+1th position
temp = ar.index(i+1,i+1)
ar.insert(i,i+1) #insert i+1 in the ith position
ar.pop(temp+1) #remove the value of i+1 from the right
c+=1 #every time you do a swap, increment the counter
print (ar) #if you want to check if ar is correct, use this print stmt
return c
a = [1,3,4,5,6,7,2,8]
print (minSwap(a))
The total number of swaps for the above example is 1. It just inserts 2 in the second place and pops out 2 from position 6.
I ran the code for a = [1,6,5,4,3,8,2,7] and it swapped in 5 moves.
I ran the code for a = [1,3,5,4,6,8,2,7] and it swapped in 3 moves.
If you are trying to figure out how this works, use a print statement right after the if statement. It will tell you the element being swapped.
From your code I take it that sorting isn't the issue here, since you know you'll end up with ar[i] == i+1. Given that, why not change your else block to swap the current element into its slot, and repeat until you ar[i] is correct.
else:
while ar[i] != i+1:
temp = ar[i]
ar[i] = ar[temp - 1]
ar[temp - 1] = temp
You don't actually need to do a sort on this array. You just need to figure out the minimum number of swaps needed. If we just look at the following pattern, we can form a hypothesis to be tested:
1234 = 0
1324 = 1, swap 2 and 3
1423 = 2, swap 2 and 4, swap 3 and 4
4213 = 2, swap 1 and 4, swap 3 and 4
4123 = 3, swap 4 and 1, swap 4 and 2, swap 4 and 3
Based on these observations, I think we can work on the hypothesis that the answer will be max(0, n - 1) where n is the count of the number of "out of place" elements.
Then the code becomes simplified to:
def minSwap(ar):
c = 0
for i in range(0, len(ar)):
if ar[i] != i+1:
c = c + 1
return c < 0 ? 0 : c
Note that I don't actually know python so don't know if that last ternary is valid in python.

find the first occurrence of a number greater than k in a sorted array

For the given sorted list,the program should return the index of the number in the list which is greater than the number which is given as input.
Now when i run code and check if it is working i am getting 2 outputs. One is the value and other output is None.
If say i gave a input of 3 for the below code.The expected output is index of 20 i.e., 1 instead i am getting 1 followed by None.
If i give any value that is greater than the one present in the list i am getting correct output i.e., "The entered number is greater than the numbers in the list"
num_to_find = int(input("Enter the number to be found"))
a=[2,20,30]
def occur1(a,num_to_find):
j = i = 0
while j==0:
if a[len(a)-1] > num_to_find:
if num_to_find < a[i]:
j=1
print(i)
break
else:
i = i + 1
else:
ret_state = "The entered number is greater than the numbers in the list"
return ret_state
print(occur1(a,num_to_find))
This code is difficult to reason about due to extra variables, poor variable names (j is typically used as an index, not a bool flag), usage of break, nested conditionals and side effect. It's also inefficient because it needs to visit each element in the list in the worst case scenario and fails to take advantage of the sorted nature of the list to the fullest. However, it appears working.
Your first misunderstanding is likely that print(i) is printing the index of the next largest element rather than the element itself. In your example call of occur1([2, 20, 30], 3)), 1 is where 20 lives in the array.
Secondly, once the found element is printed, the function returns None after it breaks from the loop, and print dutifully prints None. Hopefully this explains your output--you can use return a[i] in place of break to fix your immediate problem and meet your expectations.
Having said that, Python has a builtin module for this: bisect. Here's an example:
from bisect import bisect_right
a = [1, 2, 5, 6, 8, 9, 15]
index_of_next_largest = bisect_right(a, 6)
print(a[index_of_next_largest]) # => 8
If the next number greater than k is out of bounds, you can try/except that or use a conditional to report the failure as you see fit. This function takes advantage of the fact that the list is sorted using a binary search algorithm, which cuts the search space in half on every step. The time complexity is O(log(n)), which is very fast.
If you do wish to stick with a linear algorithm similar to your solution, you can simplify your logic to:
def occur1(a, num_to_find):
for n in a:
if n > num_to_find:
return n
# test it...
a = [2, 5, 10]
for i in range(11):
print(i, " -> ", occur1(a, i))
Output:
0 -> 2
1 -> 2
2 -> 5
3 -> 5
4 -> 5
5 -> 10
6 -> 10
7 -> 10
8 -> 10
9 -> 10
10 -> None
Or, if you want the index of the next largest number:
def occur1(a, num_to_find):
for i, n in enumerate(a):
if n > num_to_find:
return i
But I want to stress that the binary search is, by every measure, far superior to the linear search. For a list of a billion elements, the binary search will make about 20 comparisons in the worst case where the linear version will make a billion comparisons. The only reason not to use it is if the list can't be guaranteed to be pre-sorted, which isn't the case here.
To make this more concrete, you can play with this program (but use the builtin module in practice):
import random
def bisect_right(a, target, lo=0, hi=None, cmps=0):
if hi is None:
hi = len(a)
mid = (hi - lo) // 2 + lo
cmps += 1
if lo <= hi and mid < len(a):
if a[mid] < target:
return bisect_right(a, target, mid + 1, hi, cmps)
elif a[mid] > target:
return bisect_right(a, target, lo, mid - 1, cmps)
else:
return cmps, mid + 1
return cmps, mid + 1
def linear_search(a, target, cmps=0):
for i, n in enumerate(a):
cmps += 1
if n > target:
return cmps, i
return cmps, i
if __name__ == "__main__":
random.seed(42)
trials = 10**3
list_size = 10**4
binary_search_cmps = 0
linear_search_cmps = 0
for n in range(trials):
test_list = sorted([random.randint(0, list_size) for _ in range(list_size)])
test_target = random.randint(0, list_size)
res = bisect_right(test_list, test_target)[0]
binary_search_cmps += res
linear_search_cmps += linear_search(test_list, test_target)[0]
binary_search_avg = binary_search_cmps / trials
linear_search_avg = linear_search_cmps / trials
s = "%s search made %d comparisons across \n%d searches on random lists of %d elements\n(found the element in an average of %d comparisons\nper search)\n"
print(s % ("binary", binary_search_cmps, trials, list_size, binary_search_avg))
print(s % ("linear", linear_search_cmps, trials, list_size, linear_search_avg))
Output:
binary search made 12820 comparisons across
1000 searches on random lists of 10000 elements
(found the element in an average of 12 comparisons
per search)
linear search made 5013525 comparisons across
1000 searches on random lists of 10000 elements
(found the element in an average of 5013 comparisons
per search)
The more elements you add, the worse the situation looks for the linear search.
I would do something along the lines of:
num_to_find = int(input("Enter the number to be found"))
a=[2,20,30]
def occur1(a, num_to_find):
for i in a:
if not i <= num_to_find:
return a.index(i)
return "The entered number is greater than the numbers in the list"
print(occur1(a, num_to_find))
Which gives the output of 1 (when inputting 3).
The reason yours gives you 2 outputs, is because you have 2 print statements inside your code.

Number of Combinations to make change with coins

We have unlimited coins of different values - Calculate the unique combinations of how these coins can make up a specific amount. For example:
n = 4 (say, 4 cents)
coins_list = [1,2] - we have 1-cent coins, and 2-cent coins
The different combinations would be 112, 1111, and 22. (121 and 211 should be excluded since it's not unique - using one 2-cent coin and two 1-cent coin)
I have watched this video: https://www.youtube.com/watch?v=k4y5Pr0YVhg
countless number of times, and edited my codes countless number of times, but I cannot manage to get rid of the same combination of different orders.
def make_change(n, coinlist_index=None):
coin_list = [1, 2]
if coinlist_index == None:
coinlist_index = 0
#coin index position in coin_list; starts at index 0 and cycles through all the coins
if n == 0:
return 1
if n < 0:
return 0
ways = 0
# if I use for i in range(len(coin_list)), it returns an error message saying that index is out of range
for coinlist_index in range(len(coin_list)):
ways += make_change((n - coin_list[coinlist_index]), coinlist_index)
coinlist_index += 1
return ways
make_change(4)
Output: 5
My output was 5 (different ways to make change for 4 cents with 1 and 2-cent coins), instead of 3 (which is what I want).
I'm sure it has to do with the for loop toward the end, but when i change "for coinlist_index in range..." to a different iterator, i, I get an error that says index is out of range.
What is going on, and more importantly, how can I fix it?
EDIT: P.S. This is just a simple example that I'm working through to solve the actual assignment - Which is with 6 types of coins, in cents, (1, 5, 10, 25, 50, 100), and calculate how many ways to make change for 200 dollars. I have seen and tried the dynamic programming method out there, which worked, but we have to use recursion for assignment purposes.
Looks like I got it working. In each recursive pass you want to make sure that you aren't double counting possible ways to make the change. My thought to do this was to make sure that you never go backwards in the coin_list. So for the coin_list [1,2] if we ever use the 2 cent coin we never want the option to use the 1 cent coin afterwards. I made sure it follows this order by changing your for loop a bit:
for i in range(len(coin_list)-coinlist_index):
ways += make_change((n - coin_list[i+coinlist_index-1]), coinlist_index)
In the for loop I subtracted coinlist_index from the upper bound so we don't cycle over all coins once the index reaches 1, then added the index to where you pull from the coin_list, making sure once coinlist_index is 1 or more, we NEVER usecoin_list[0]. This got me to 3 in your sample case, hopefully it works for all cases. Full code:
def make_change(n, coinlist_index=None):
coin_list = [1, 2]
if coinlist_index == None:
coinlist_index = 0
#coin index position in coin_list; starts at index 0 and cycles through all the coins
if n == 0:
return 1
if n < 0:
return 0
ways = 0
# if I use for i in range(len(coin_list)), it returns an error message saying that index is out of range
for i in range(len(coin_list)-coinlist_index):
ways += make_change((n - coin_list[i+coinlist_index-1]), coinlist_index)
coinlist_index += 1
return ways
print(make_change(4))
I feel 5 is actually the correct answer.
1 1 1 1
1 1 2
1 2 1
2 1 1
2 2
Or if you want distinct result, you may store results in the list and remove the duplicate result.
def make_change(n, coinlist_index=0):
coin_list = [1, 2]
if n == 0:
return [[]]
if n < 0:
return []
ways = []
for coinlist_index in range(len(coin_list)):
res = make_change((n - coin_list[coinlist_index]), coinlist_index)
ways += list(map(lambda x : x + [coin_list[coinlist_index]], res))
return ways
def remove_dup(lolist):
res = []
for lst in lolist:
lst.sort()
if lst not in res:
res.append(lst)
return res
print remove_dup(make_change(4))

Python: How to display all print statements in a if-else statement

This is one of the lab questions: I try to create a program that generates a list of N random integers between 0 and 19 and computes the element strictly less than 5, 10, 15 and 20. I want to print all of the 'There are {} elements between x and y' statements.
When I run the program, it only shows the first one, and not the others. How do I correct it?
from random import randint
import sys
while True:
nb_of_elements = input('How many element do you want to generate? ')
try:
nb_of_elements = int(nb_of_elements)
break
except ValueError:
print('Input is not an integer, try again...')
L = [randint(0, 19) for _ in range (nb_of_elements)]
print('The list is :', L)
number = [0] * 4
for i in range (nb_of_elements):
number[L[i] // 5]+=1
for i in range(4):
if number[i] < 5:
print('There are {} elements between 0 and 4'.format (number[i]))
elif 5<= number[i] < 10:
print('There are {} elements between 5 and 9'.format(number[i]))
elif 10<= number[i] < 15:
print('There are {} elements between 10 and 14'.format(number[i]))
else:
print('There are {} elements between 15 and 20'.format(number[i]))
Your mistake is that you're attempting to count numbers in a range twice.
First, you use the trick with integer division:
for i in range (nb_of_elements):
number[L[i] // 5]+=1
So, number already contains the count of elements in the ranges 0--4, 5--9, 10--14 and 15--19 (inclusive).
Then, in your if-elif-elif-else block, you look at the value of number, whether it fits in any of these ranges. number, however, contains counts. On average, it will contain about nb_of_elements / 5 counts for each element.
You don't need the if-elif-elif-else block. Instead, loop through range(4) as you do know, and print each element number[i]. Each time, it'll correspond to the next range (you may need some smart thing to print the range. 5*i and 5*i+4 may do that).
It's kind-of interesting that you came up with a smart way to count the numbers in a range (number[L[i]//5] += 1), and then fell back to standard range comparison in an if-elif-else chain. I guess one can outsmart oneself.
You already have found a smart way to count fill the nb_of_elements list. Now you may want a smart way to print it. You can use enumerate to get the current index in the for loop: with this index, you can create the 'between X and Y' variables.
counts = [0] * 4
for i in range (nb_of_elements):
counts[L[i] // 5]+=1
# Loop the counts, and keep track of the index for enumerate
for i,count in enumerate(counts):
# i * 5 will be [0,5,10,15] and i * 5 + 5 will be [5,10,15,20]
print('There are {} elements between {} and {}'.format (count, i*5, i*5 + 5))
#The list is : [7, 10, 5]
#There are 0 elements between 0 and 5
#There are 2 elements between 5 and 10
#There are 1 elements between 10 and 15
#There are 0 elements between 15 and 20
In Python, a range is exclusive, meaning 'between 0 and 5' is [0,1,2,3,4]. I have chosen this notation for the print function as well: it now states 'between 0 and 5' (exclusive) instead of 'between 0 and 4' (inclusive) like you used in your code. This can of course be easily changed: i*5 + 5 > i*5 + 4.

Resources