Trying to implement a finder function using recursion in python - python

I am trying to fine the kth smallest value in x selected using a random splitter,
using recusion, I have already done this without recursion but I am explicitly trying to find it with using recursion. I am faced with a x = x[1,len(x)] ValueError: ValueError: empty range for randrange() (0,0, 0)
splitter = random.randrange(0,len(x))
ants = x[splitter]
lilz = []
bigz = []
for pigs in x:
if pigs >= ants:
bigz.append(pigs)
elif pigs == ants:
splitter
else:
lilz.append(pigs)
if k == splitter:
s = x[splitter]
elif k < splitter:
s = selectR(lilz,k)
else:
s = selectR(bigz, k - ( len(lilz) + 1))
return s

Rather than looping over the values in x and splitting it into two lists, just sort x the first time it's given to us, and then your evaluations become easier. You COULD sort it every time, but if we add a 3rd parameter defaulted to False, we can indicate on recursive calls that it's already sorted and save some work:
import random
def selR(x, k, is_sorted=False):
"""Returns: The kth smallest value in x selected using a random splitter,
using RECURSION.
Parameter x: list of values
Precondition: x contains ints or floats and len(x) > 0
Parameter k: the rank of the value we want to find
Precondition: k is an int in [1..n] where n is the length of x."""
if not is_sorted:
x = sorted(x)
if len(x) == 1 or k <= 1:
"""
1) Does x only have one item? Then it doesn't
really matter what k is, we're done.
2) Is k <= 1? If so, then we don't need to split anymore -
the head of the sorted list is the desired value.
"""
return x[0]
"""3) Find a splitter in the defined range - [1..n] where n is the length of x"""
splitter = random.randrange(1,len(x))
if splitter == k:
"""
4) Is splitter the same as k? If so, we've found our value;
just return the tail of split list. return x[k-1]
"""
return x[k-1]
elif splitter > k:
"""
5) Is splitter larger than k? If so, the kth smallest value is found
before the split, so we recurse with selR(x[1:splitter], k-1) - we
can start at x[1] and reduce k by one, because if x[0] was the
kth smallest value, we would have already returned it in step 2.
"""
return selR(x[1:splitter], k-1, True)
else:
"""
6) Is splitter smaller than k? Then the kth smallest value is found
after the split return selR(x[splitter:], k-len(x[0:splitter])) -
that is, remove items before splitter from x and reduce k by the number
of items we just removed.
"""
return selR(x[splitter:], k-len(x[0:splitter]), True)
# Test case
x = [1,2,1,3,4,5]
for k in range(1,len(x)+1):
ord = { 1: "st", 2: "nd", 3: "rd" }.get(k if (k < 20) else (k % 10), 'th')
print(f"{k}{ord} smallest element of {x}:", selR(x, k))

Related

Check if sum of subarray is a multiple of target

I encountered this problem, where I have to check if there exists a subarray sum that is a multiple of a target value, and if the length of the subarray is at least 2.
I've looked at one of the solution, which is below
class Solution():
def checkSubarraySum(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
dic = {0:-1}
summ = 0
for i, n in enumerate(nums):
if k != 0:
summ = (summ + n) % k
else:
summ += n
if summ not in dic:
dic[summ] = i
else:
if i - dic[summ] >= 2:
return True
return False
What I don't understand is why it is "i - dic[summ] >= 2" instead of greater or equal to 1? I assume it is checking whether the length is greater than 2, so wouldn't the difference of two indexes plus one be the length of the subarray?
Since all the elements in nums are either 0 or positive, the condition summ in dic (else of summ not in dic) and i - dic[summ] >= 2 only satisfy when the line summ = (summ + n) % k is run in the same iteration. The reason we need to check why it's >=2 and not >=1 is because the index in dic[summ] is not included in the subarray so i - dic[summ] is to check if the size of the continuous subarray is at least 2

Getting all subsets from subset sum problem on Python using Dynamic Programming

I am trying to extract all subsets from a list of elements which add up to a certain value.
Example -
List = [1,3,4,5,6]
Sum - 9
Output Expected = [[3,6],[5,4]]
Have tried different approaches and getting the expected output but on a huge list of elements it is taking a significant amount of time.
Can this be optimized using Dynamic Programming or any other technique.
Approach-1
def subset(array, num):
result = []
def find(arr, num, path=()):
if not arr:
return
if arr[0] == num:
result.append(path + (arr[0],))
else:
find(arr[1:], num - arr[0], path + (arr[0],))
find(arr[1:], num, path)
find(array, num)
return result
numbers = [2, 2, 1, 12, 15, 2, 3]
x = 7
subset(numbers,x)
Approach-2
def isSubsetSum(arr, subset, N, subsetSize, subsetSum, index , sum):
global flag
if (subsetSum == sum):
flag = 1
for i in range(0, subsetSize):
print(subset[i], end = " ")
print("")
else:
for i in range(index, N):
subset[subsetSize] = arr[i]
isSubsetSum(arr, subset, N, subsetSize + 1,
subsetSum + arr[i], i + 1, sum)
If you want to output all subsets you can't do better than a sluggish O(2^n) complexity, because in the worst case that will be the size of your output and time complexity is lower-bounded by output size (this is a known NP-Complete problem). But, if rather than returning a list of all subsets, you just want to return a boolean value indicating whether achieving the target sum is possible, or just one subset summing to target (if it exists), you can use dynamic programming for a pseudo-polynomial O(nK) time solution, where n is the number of elements and K is the target integer.
The DP approach involves filling in an (n+1) x (K+1) table, with the sub-problems corresponding to the entries of the table being:
DP[i][k] = subset(A[i:], k) for 0 <= i <= n, 0 <= k <= K
That is, subset(A[i:], k) asks, 'Can I sum to (little) k using the suffix of A starting at index i?' Once you fill in the whole table, the answer to the overall problem, subset(A[0:], K) will be at DP[0][K]
The base cases are for i=n: they indicate that you can't sum to anything except for 0 if you're working with the empty suffix of your array
subset(A[n:], k>0) = False, subset(A[n:], k=0) = True
The recursive cases to fill in the table are:
subset(A[i:], k) = subset(A[i+1:, k) OR (A[i] <= k AND subset(A[i+i:], k-A[i]))
This simply relates the idea that you can use the current array suffix to sum to k either by skipping over the first element of that suffix and using the answer you already had in the previous row (when that first element wasn't in your array suffix), or by using A[i] in your sum and checking if you could make the reduced sum k-A[i] in the previous row. Of course, you can only use the new element if it doesn't itself exceed your target sum.
ex: subset(A[i:] = [3,4,1,6], k = 8)
would check: could I already sum to 8 with the previous suffix (A[i+1:] = [4,1,6])? No. Or, could I use the 3 which is now available to me to sum to 8? That is, could I sum to k = 8 - 3 = 5 with [4,1,6]? Yes. Because at least one of the conditions was true, I set DP[i][8] = True
Because all the base cases are for i=n, and the recurrence relation for subset(A[i:], k) relies on the answers to the smaller sub-problems subset(A[i+i:],...), you start at the bottom of the table, where i = n, fill out every k value from 0 to K for each row, and work your way up to row i = 0, ensuring you have the answers to the smaller sub-problems when you need them.
def subsetSum(A: list[int], K: int) -> bool:
N = len(A)
DP = [[None] * (K+1) for x in range(N+1)]
DP[N] = [True if x == 0 else False for x in range(K+1)]
for i in range(N-1, -1, -1):
Ai = A[i]
DP[i] = [DP[i+1][k] or (Ai <=k and DP[i+1][k-Ai]) for k in range(0, K+1)]
# print result
print(f"A = {A}, K = {K}")
print('Ai,k:', *range(0,K+1), sep='\t')
for (i, row) in enumerate(DP): print(A[i] if i < N else None, *row, sep='\t')
print(f"DP[0][K] = {DP[0][K]}")
return DP[0][K]
subsetSum([1,4,3,5,6], 9)
If you want to return an actual possible subset alongside the bool indicating whether or not it's possible to make one, then for every True flag in your DP you should also store the k index for the previous row that got you there (it will either be the current k index or k-A[i], depending on which table lookup returned True, which will indicate whether or not A[i] was used). Then you walk backwards from DP[0][K] after the table is filled to get a subset. This makes the code messier but it's definitely do-able. You can't get all subsets this way though (at least not without increasing your time complexity again) because the DP table compresses information.
Here is the optimized solution to the problem with a complexity of O(n^2).
def get_subsets(data: list, target: int):
# initialize final result which is a list of all subsets summing up to target
subsets = []
# records the difference between the target value and a group of numbers
differences = {}
for number in data:
prospects = []
# iterate through every record in differences
for diff in differences:
# the number complements a record in differences, i.e. a desired subset is found
if number - diff == 0:
new_subset = [number] + differences[diff]
new_subset.sort()
if new_subset not in subsets:
subsets.append(new_subset)
# the number fell short to reach the target; add to prospect instead
elif number - diff < 0:
prospects.append((number, diff))
# update the differences record
for prospect in prospects:
new_diff = target - sum(differences[prospect[1]]) - prospect[0]
differences[new_diff] = differences[prospect[1]] + [prospect[0]]
differences[target - number] = [number]
return subsets

How to make the output return a number?

I created this code so that the program will print out all numbers within range (1000 to 10,000) if it is divisible by value k, as set below, but the ouput yields none.. what am I doing wrong?
k = 6
def pincode(k: int):
for x in range(1000,10000):
if x // k == 0:
print(x)
print(pincode(k))
what am I supposed to change to make sure that the code prints out all numbers within the range divisible by k?
There are two bugs, here for printing the function, you need to return value. If you've written print already then just call the function. If you want to print k for x%k==0 then x has multiple values. You can return multiple values by collecting x values to list. The second one is, it is x%k==0 and not x//k==0. // gives you whole number quotient and % will give you remainder. Eg, 49//7 is 7 and 49%7 is 0 and 26//7 is 3 and 26%7 is 5. Your new code:
k = 6
def pincode(k: int):
collect=[]
for x in range(1000,10000):
if x % k == 0:
collect.append(x)
return collect
print(pincode(k))
You can use a single comprehension for such a task.
k = 6
print([x for x in range(1000, 10000) if x % k == 0])
I think you should try changing the // in if x // k == 0: to % which is an operator that returns the remainder instead of the quotient.
Your function pincode(k) doesn't have a return argument, so it returns none. Append the values to a list, then add that list to the return argument.
k = 6
def pincode(k: int):
a = [] #empty list
for x in range(1000,10000):
if x % k == 0: # use % instead of //
a.append(x) # append x to list
return a #return the list
print(pincode(k))
The double forward slash in Python is known as the integer division operator. Essentially, it will divide the left by the right, and only keep the whole number component.
I would suggest to use % to find if the number is divisible.
k = 6
def pincode(k: int):
for x in range(1000,10000):
#print(f"x and k {x} and {k} res {x%k}")
if x % k == 0:
print(x)
print(pincode(k))

Binary strings recursive function

How to write a recursive function that generates a list of binary of length n with a specified number of 1s?
Here's a code that generates recursively a list of binarys; without a specified number of 1s:
def generateAllBinaryStrings(n, arr, i):
if i == n:
printTheArray(arr, n)
return
# First assign "0" at ith position
# and try for all other permutations
# for remaining positions
arr[i] = 0
generateAllBinaryStrings(n, arr, i + 1)
# And then assign "1" at ith position
# and try for all other permutations
# for remaining positions
arr[i] = 1
generateAllBinaryStrings(n, arr, i + 1)
Taken from geeksforgeeks
You could do it like this:
def binaryStrings(n, ones):
if n < ones: # impossible
return []
if n == ones:
return ["1" * ones]
if ones == 0:
return ["0" * n]
a = binaryStrings(n-1, ones)
b = binaryStrings(n-1, ones-1)
return ["0"+s for s in a] + ["1"+s for s in b]
Example call to get all 6-digit binary numbers which have exactly 4 1-digits:
print(binaryStrings(6,4))
You have to generate all possible sequences with adding a 0 or a 1 at each position. Total possible sequences = 2^MAX. Keep track of the number of 1s in the current sequence so far to break.
# Generate all binary numbers with exactly "n" 1s
# Max digits in the binary number = MAX
def binary(n):
MAX = 5
all_solutions = []
def solve(current, remaining_ones):
if len(current) > MAX:
return
if remaining_ones == 0:
all_solutions.append(current+"0"*(MAX-len(current)))
return
solve(current+"1", remaining_ones - 1)
solve(current+"0", remaining_ones)
solve("", n)
return all_solutions
print(binary(2))
# ['11000', '10100', '10010', '10001', '01100', '01010', '01001', '00110', '00101', '00011']

How to see if the list contains consecutive numbers

I want to test if a list contains consecutive integers and no repetition of numbers.
For example, if I have
l = [1, 3, 5, 2, 4, 6]
It should return True.
How should I check if the list contains up to n consecutive numbers without modifying the original list?
I thought about copying the list and removing each number that appears in the original list and if the list is empty then it will return True.
Is there a better way to do this?
For the whole list, it should just be as simple as
sorted(l) == list(range(min(l), max(l)+1))
This preserves the original list, but making a copy (and then sorting) may be expensive if your list is particularly long.
Note that in Python 2 you could simply use the below because range returned a list object. In 3.x and higher the function has been changed to return a range object, so an explicit conversion to list is needed before comparing to sorted(l)
sorted(l) == range(min(l), max(l)+1))
To check if n entries are consecutive and non-repeating, it gets a little more complicated:
def check(n, l):
subs = [l[i:i+n] for i in range(len(l)) if len(l[i:i+n]) == n]
return any([(sorted(sub) in range(min(l), max(l)+1)) for sub in subs])
The first code removes duplicates but keeps order:
from itertools import groupby, count
l = [1,2,4,5,2,1,5,6,5,3,5,5]
def remove_duplicates(values):
output = []
seen = set()
for value in values:
if value not in seen:
output.append(value)
seen.add(value)
return output
l = remove_duplicates(l) # output = [1, 2, 4, 5, 6, 3]
The next set is to identify which ones are in order, taken from here:
def as_range(iterable):
l = list(iterable)
if len(l) > 1:
return '{0}-{1}'.format(l[0], l[-1])
else:
return '{0}'.format(l[0])
l = ','.join(as_range(g) for _, g in groupby(l, key=lambda n, c=count(): n-next(c)))
l outputs as: 1-2,4-6,3
You can customize the functions depending on your output.
We can use known mathematics formula for checking consecutiveness,
Assuming min number always start from 1
sum of consecutive n numbers 1...n = n * (n+1) /2
def check_is_consecutive(l):
maximum = max(l)
if sum(l) == maximum * (maximum+1) /2 :
return True
return False
Once you verify that the list has no duplicates, just compute the sum of the integers between min(l) and max(l):
def check(l):
total = 0
minimum = float('+inf')
maximum = float('-inf')
seen = set()
for n in l:
if n in seen:
return False
seen.add(n)
if n < minimum:
minimum = n
if n > maximum:
maximum = n
total += n
if 2 * total != maximum * (maximum + 1) - minimum * (minimum - 1):
return False
return True
import numpy as np
import pandas as pd
(sum(np.diff(sorted(l)) == 1) >= n) & (all(pd.Series(l).value_counts() == 1))
We test both conditions, first by finding the iterative difference of the sorted list np.diff(sorted(l)) we can test if there are n consecutive integers. Lastly, we test if the value_counts() are all 1, indicating no repeats.
I split your query into two parts part A "list contains up to n consecutive numbers" this is the first line if len(l) != len(set(l)):
And part b, splits the list into possible shorter lists and checks if they are consecutive.
def example (l, n):
if len(l) != len(set(l)): # part a
return False
for i in range(0, len(l)-n+1): # part b
if l[i:i+3] == sorted(l[i:i+3]):
return True
return False
l = [1, 3, 5, 2, 4, 6]
print example(l, 3)
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
return False
else:
if counter[element-1] != 0:
return False
else:
counter[element-1] = 1
return True
The input to this function is your list.This function returns False if the numbers are repeated.
The below code works even if the list does not start with 1.
def check_is_consecutive(l):
"""
sorts the list and
checks if the elements in the list are consecutive
This function does not handle any exceptions.
returns true if the list contains consecutive numbers, else False
"""
l = list(filter(None,l))
l = sorted(l)
if len(l) > 1:
maximum = l[-1]
minimum = l[0] - 1
if minimum == 0:
if sum(l) == (maximum * (maximum+1) /2):
return True
else:
return False
else:
if sum(l) == (maximum * (maximum+1) /2) - (minimum * (minimum+1) /2) :
return True
else:
return False
else:
return True
1.
l.sort()
2.
for i in range(0,len(l)-1)))
print(all((l[i+1]-l[i]==1)
list must be sorted!
lst = [9,10,11,12,13,14,15,16]
final = True if len( [ True for x in lst[:-1] for y in lst[1:] if x + 1 == y ] ) == len(lst[1:]) else False
i don't know how efficient this is but it should do the trick.
With sorting
In Python 3, I use this simple solution:
def check(lst):
lst = sorted(lst)
if lst:
return lst == list(range(lst[0], lst[-1] + 1))
else:
return True
Note that, after sorting the list, its minimum and maximum come for free as the first (lst[0]) and the last (lst[-1]) elements.
I'm returning True in case the argument is empty, but this decision is arbitrary. Choose whatever fits best your use case.
In this solution, we first sort the argument and then compare it with another list that we know that is consecutive and has no repetitions.
Without sorting
In one of the answers, the OP commented asking if it would be possible to do the same without sorting the list. This is interesting, and this is my solution:
def check(lst):
if lst:
r = range(min(lst), max(lst) + 1) # *r* is our reference
return (
len(lst) == len(r)
and all(map(lst.__contains__, r))
# alternative: all(x in lst for x in r)
# test if every element of the reference *r* is in *lst*
)
else:
return True
In this solution, we build a reference range r that is a consecutive (and thus non-repeating) sequence of ints. With this, our test is simple: first we check that lst has the correct number of elements (not more, which would indicate repetitions, nor less, which indicates gaps) by comparing it with the reference. Then we check that every element in our reference is also in lst (this is what all(map(lst.__contains__, r)) is doing: it iterates over r and tests if all of its elements are in lts).
l = [1, 3, 5, 2, 4, 6]
from itertools import chain
def check_if_consecutive_and_no_duplicates(my_list=None):
return all(
list(
chain.from_iterable(
[
[a + 1 in sorted(my_list) for a in sorted(my_list)[:-1]],
[sorted(my_list)[-2] + 1 in my_list],
[len(my_list) == len(set(my_list))],
]
)
)
)
Add 1 to any number in the list except for the last number(6) and check if the result is in the list. For the last number (6) which is the greatest one, pick the number before it(5) and add 1 and check if the result(6) is in the list.
Here is a really short easy solution without having to use any imports:
range = range(10)
L = [1,3,5,2,4,6]
L = sorted(L, key = lambda L:L)
range[(L[0]):(len(L)+L[0])] == L
>>True
This works for numerical lists of any length and detects duplicates.
Basically, you are creating a range your list could potentially be in, editing that range to match your list's criteria (length, starting value) and making a snapshot comparison. I came up with this for a card game I am coding where I need to detect straights/runs in a hand and it seems to work pretty well.

Categories