Given a list of unsorted integers and a target integer, find out if any pair's difference in the list is equal to the target integer with recursion.
>>> aList = [5, 4, 8, -3, 6]
>>> target = 9
return True
>>> aList = [-1, 5, 4]
>>> target = 3
return False
For and while loops are not allowed.
No imports allowed.
.sort() is not allowed.
I tried this and it didn't work.
def calculate(aList, target):
if len(aList) == 0 and diff != 0:
return False
startIndex = 0
endIndex = len(aList) - 1
return resursive_sum(aList, target, startIndex, endIndex)
def resursive_sum(aList, targ, start, end):
print(f'Start: {start}')
print(f'End: {end}')
if start == end:
return False
elif aList[end] - aList[start] == targ:
return True
elif aList[end] - aList[start] < targ:
return resursive_sum(values, targ, start, end - 1)
return resursive_sum(aList, targ, start + 1, end)
I'm unsure of how this problem could be solved if we aren't able to use loops to sort the list. Even if we could use recursion to sort the list, how should the recursion look so that it can scan every pair's difference?
So I actually implemented it, but for educational purposes I'm not gonna post it until a bit later (I'll update it in a few hours) as I assume this is for a class or some other setting where you should figure it out on your own.
Assume you are trying to hit a difference target t = 5 and you are evaluating an arbitrary element 8. There are only two values that would allow 8 to have a complement in the set: 8 + 5 = 13 and 8 - 5 = 3.
If 3 or 13 had been in any previous elements, you would know that the set has a pair of complements. Otherwise, you'd want to record the fact that 8 had been seen. Thereby, if 3 was found later, 8 would be queried as 3 + 5 = 8 would be considered.
In other words, I am proposing a method where you recursively traverse the list and either
(base case) Are at the end of the list
Have a current element a such that a + t or a - t has been seen
Record that the current element has been seen and go to the next element
Ideally, this should have O(n) time complexity and O(n) space complexity in the worst case (assuming efficient implementation with pass-by-reference or similar, and also amortized constant-time set query). It can also be implemented using a basic array, but I'm not going to say that's better (in python).
I'll post my solution in a few hours. Good luck!
EDIT 1: Hopefully, you had enough time to get it to work. The method I described can be done as follows:
def hasDiffRecur(L, t, i, C):
"""
Recursive version to see if list has difference
:param L: List to be considered
:param t: Target difference
:param i: Current index to consider
:param C: Cache set
"""
# We've reached the end. Give up
if i >= len(L):
return False
print(f" > L[{i}] = {L[i]:2}; is {L[i]-t:3} or {L[i]+t:2} in {C}")
# Has the complement been cached?
if L[i] - t in C:
print(f"! Difference between {L[i]} and {L[i]-t} is {t}")
return True
if L[i] + t in C:
print(f"! Difference between {L[i]} and {L[i]+t} is {t}")
return True
# Complement not seen yet. Cache element and go to next element
C.add(L[i])
return hasDiffRecur(L, t, i+1, C)
###################################################################
def hasDiff(L, t):
"""
Initialized call for hasDiffRecur. Also prints intro message.
See hasDiffRecur for param info
"""
print(f"\nIs a difference of {t} present in {L}?")
return hasDiffRecur(L, t, 0, set())
###################################################################
hasDiff([5, 4, 8, -3, 6], 9)
hasDiff([-1, 5, 4], 3)
hasDiff([-1, 5, 4, -1, 7], 0) # If concerned about set non-duplicity
OUTPUT:
Is a difference of 9 present in [5, 4, 8, -3, 6]?
> L[0] = 5; is -4 or 14 in set()
> L[1] = 4; is -5 or 13 in {5}
> L[2] = 8; is -1 or 17 in {4, 5}
> L[3] = -3; is -12 or 6 in {8, 4, 5}
> L[4] = 6; is -3 or 15 in {8, -3, 4, 5}
! Difference between 6 and -3 is 9
Is a difference of 3 present in [-1, 5, 4]?
> L[0] = -1; is -4 or 2 in set()
> L[1] = 5; is 2 or 8 in {-1}
> L[2] = 4; is 1 or 7 in {5, -1}
Is a difference of 0 present in [-1, 5, 4, -1, 7]?
> L[0] = -1; is -1 or -1 in set()
> L[1] = 5; is 5 or 5 in {-1}
> L[2] = 4; is 4 or 4 in {5, -1}
> L[3] = -1; is -1 or -1 in {4, 5, -1}
! Difference between -1 and -1 is 0
EDIT 2:
This is a pretty clever and efficient solution. I do realize that maybe it is the intention to not allow any traversal at all (i.e. no existance querying for set). If that is the case, the above approach can be done with a constant-size list that is pre-allocated to size equal to the range of the values of the list.
If the notion of pre-allocating to the size of the range of the list is still too much iteration, I can think of the exhaustive approach implemented recursively. There is likely a more efficient approach for this, but you could boil the problem down to a double-for-loop-like problem (O(n^2) time complexity). This is a trivial algorithm and I think you can understand it without documentation, so I'll just throw it in there to be complete:
def hasDiffRecur(L, t, i = 0, j = 1):
if i >= len(L): return False
if j >= len(L): return hasDiffRecur(L, t, i+1, i+2)
if abs(L[i] - L[j]) == t: return True
return hasDiffRecur(L, t, i, j+1)
###################################################################
print(hasDiffRecur([5, 4, 8, -3, 6], 9)) # True
print(hasDiffRecur([-1, 5, 4], 3)) # False
print(hasDiffRecur([-1, 5, 4, -1, 7], 0)) # True
choose
I'll start with a generic function that takes a list, t, and a number of elements to choose, n -
def choose(t, n):
if n == 0:
return [[]]
elif not t:
return []
else:
return append \
( map \
( choose(rest(t), n - 1)
, lambda c: append([first(t)], c)
)
, choose(rest(t), n)
)
print(choose(["a", "b", "c", "d"], 2))
[['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]
helpers
Your question imposes quite a few restrictions and Python is a multi-paradigm language and so we're going to use a number of helpers to make things readable
def first(t):
return t[0]
def rest(t):
return t[1:]
def append(t0, t1):
return t0 + t1
I don't know if map counts as an import, but we will define our own just in case -
def map(t, f):
if not t:
return []
else:
return append \
( [f(first(t))]
, map(rest(t), f)
)
solve
Great, now that we've finished implementing choose, let's see how we can apply it to our problem
print(choose([5, 4, 8, -3, 6], 2))
[[5, 4], [5, 8], [5, -3], [5, 6], [4, 8], [4, -3], [4, 6], [8, -3], [8, 6], [-3, 6]]
As you can see, we've found all combinations of 2 elements. We just need to loop through these and check if a pair can be subtracted to reach our target, q -
def solve(t, q):
def check(p):
(x, y) = p
return x - y == q or y - x == q
def loop(c):
if not c:
return False
else:
return check(first(c)) or loop(rest(c))
return loop(choose(t, 2))
print(solve([5, 4, 8, -3, 6], 9))
print(solve([-1, 5, 4], 3))
True
False
allowing for
This is a great exercise to build your recursion skills. Disallowing for is the most challenging restriction to overcome. Here's what it could look like if we could use it -
def choose(t, n):
if n == 0:
yield []
elif not t:
return
else:
for c in choose(t[1:], n - 1):
yield [t[0]] + c
yield from choose(t[1:], n)
def solve(t, q):
for (x,y) in choose(t, 2):
if x - y == q or y - x == q:
return True
return False
print(solve([5, 4, 8, -3, 6], 9))
print(solve([-1, 5, 4], 3))
True
False
This variant has an added advantage that it will stop computing combinations as soon as a solution is found. The first variant must compute all combinations first and then begin iterating through them.
allowing other built-ins
Python built-in functions include map and any and offer us another way to get around the for restriction, but I'm unsure if those are allowed -
def choose(t, n):
if n == 0:
yield []
elif not t:
return
else:
yield from map \
( lambda c: [t[0]] + c
, choose(t[1:], n - 1)
)
yield from choose(t[1:], n)
def solve(t, q):
def check(p):
(x,y) = p
return x - y == q or y - x == q
return any(map(check, choose(t, 2)))
print(solve([5, 4, 8, -3, 6], 9))
print(solve([-1, 5, 4], 3))
True
False
Problem:
given an array of int aList and int target,
check if the difference between each element in aList equals to target
use recursion
do not use .sort()
do not use while and for
do not use import
Example:
>>> aList = [5, 4, 8, -3, 6]
>>> target = 9
return True
>>> aList = [-1, 5, 4]
>>> target = 3
return False
Comparing the differences:
5 4 8 -3 6
-------------------------
5 | X
4 | 1 X
8 | 3 4 X
-3 | 6 7 11 X
6 | 1 2 2 9 X
where X means that there's no difference (same number)
since we find 9 there, so it should return True (target is 9)
Traditional for loops
To solve recursion problem, first try to solve it with traditional for loops:
def compareAll(lst, tgt):
for x in lst: # let's call this x loop
for y in lst: # let's call this y loop
if abs(x-y) == tgt:
return True
return False
print( compareAll([5,4,8,-3,6],9) )
print( compareAll([-1,5,4],3) )
This returns True then False
Recursion
Now we can try using recursion loop. Since we already got the for loop, we can convert it like this:
def compareAll(lst, tgt, x=0, y=0):
if(len(lst)-1 == x and len(lst) == y):
return False
if(len(lst) == x or len(lst) == y):
return compareAll(lst, tgt, x+1, 0)
if(abs(lst[x] - lst[y])==tgt):
return True
return compareAll(lst, tgt, x, y+1)
print( compareAll([5,4,8,-3,6],9) )
print( compareAll([-1,5,4],3) )
How I convert for loop into this:
python's for loop is actually foreach loop in most other languages
so, pure for loop in python will be like:
def compareAll(lst, tgt):
x = 0
while x < len(lst): # let's call this x loop
y = 0
while y < len(lst): # let's call this y loop
if abs(lst[x]-lst[y]) == tgt:
return True
y = y+1
x = x+1
return False
print( compareAll([5,4,8,-3,6],9) )
print( compareAll([-1,5,4],3) )
notice the stopping condition of x loop: when all the array element have been looped
so we add stopping condition here: if(len(lst)-1 == x and len(lst) == y): return False
notice the stopping condition of y loop: when all the array element have been looped
so we add stopping condition here: if(len(lst) == x or len(lst) == y): return compareAll(lst, tgt, x+1, 0)
this stops the current y loop and continue with the x loop
then, we add the actual content of the loop: if(abs(lst[x] - lst[y])==tgt): return True
last, we have to continue the loop: return compareAll(lst, tgt, x, y+1)
The key to convert for loop into recursive loop is just to identify when the loop should end, and when the loop should continue.
This should work and is quite concise:
def q(target, aList, memo=set()):
if len(aList) == 0:
return False
num = aList.pop()
memo.add(num)
if target + num in memo:
return True
return q(target, aList, memo)
q(target=9, aList=[5, 4, 8, -3, 6]) # True
q(target=3, aList=[-1,5,4]) # False
The key insight for me is that a target t and a given number n, the difference d is known. Dict/set/hashmaps are fast at detecting membership, regardless of how many items are added. So... just pop through the list of values and chuck them into a hashmap for later comparison.
The problem can be solved by checking every possible pair of numbers in the list for a solution. If you are allowed to use Python's standard library, then the solution is pretty straight forward.
from itertools import product
def check(xs, target):
return any(map(lambda x: x[0]-x[1] == target, product(xs, xs)))
Breakdown
product(xs, xs) gives the cross product of xs with itself
any(iterable) returns true if any element of iterable is truthy
map(function, iterable) lazily (applies function to every element of iterable)
lambda arg_tuple: expression annonymous function with arguments arg_tuple and returns the result of expression
The return statement in check uses lazy structures so it only does as much work as
is needed, and is space efficient.
Assuming that this is just an exercise in recursion, it probably doesn't preclude a "brute force" approach. You can use the recursion to pair up every value with the remaining ones until you find a matching difference.
For example:
def hasDiff(L,diff,base=None):
if not L: return False # empty list: no match
if base is None: # search with & without first as base
return hasDiff(L[1:],diff,L[0]) or hasDiff(L[1:],diff)
return abs(base-L[0]) == diff or hasDiff(L[1:],diff,base) # match or recurse
print(hasDiff([5, 4, 8, -3, 6],9)) # True
print(hasDiff([-1, 5, 4],3)) # False
When the function recurses with a base value, it merely checks the first item in the remainder of the list and recurses for the other values. When the function recurses without a base value, it tries to find new pairs that don't involve the first item (i.e. in the remainder of the list)
I'm currently trying to get an array of numbers like this one randomly shuffled:
label_array = np.repeat(np.arange(6), 12)
The only constrain is that no consecutive elements of the shuffle must be the same number. For that I'm currently using this code:
# Check if there are any occurrences of two consecutive
# elements being of the same category (same number)
num_occurrences = np.sum(np.diff(label_array) == 0)
# While there are any occurrences of this...
while num_occurrences != 0:
# ...shuffle the array...
np.random.shuffle(label_array)
# ...create a flag for occurrences...
flag = np.hstack(([False], np.diff(label_array) == 0))
flag_array = label_array[flag]
# ...and shuffle them.
np.random.shuffle(flag_array)
# Then re-assign them to the original array...
label_array[flag] = flag_array
# ...and check the number of occurrences again.
num_occurrences = np.sum(np.diff(label_array) == 0)
Although this works for an array of this size, I don't know if it would work for much bigger arrays. And even so, it may take a lot of time.
So, is there a better way of doing this?
May not be technically the best answer, hopefully it suffices for your requirements.
import numpy as np
def generate_random_array(block_length, block_count):
for blocks in range(0, block_count):
nums = np.arange(block_length)
np.random.shuffle(nums)
try:
if nums[0] == randoms_array [-1]:
nums[0], nums[-1] = nums[-1], nums[0]
except NameError:
randoms_array = []
randoms_array.extend(nums)
return randoms_array
generate_random_array(block_length=1000, block_count=1000)
Here is a way to do it, for Python >= 3.6, using random.choices, which allows to choose from a population with weights.
The idea is to generate the numbers one by one. Each time we generate a new number, we exclude the previous one by temporarily setting its weight to zero. Then, we decrement the weight of the chosen one.
As #roganjosh duly noted, we have a problem at the end when we are left with more than one instance of the last value - and that can be really frequent, especially with a small number of values and a large number of repeats.
The solution I used is to insert these value back into the list where they don't create a conflict, with the short send_back function.
import random
def send_back(value, number, lst):
idx = len(lst)-2
for _ in range(number):
while lst[idx] == value or lst[idx-1] == value:
idx -= 1
lst.insert(idx, value)
def shuffle_without_doubles(nb_values, repeats):
population = list(range(nb_values))
weights = [repeats] * nb_values
out = []
prev = None
for i in range(nb_values * repeats):
if prev is not None:
# remove prev from the list of possible choices
# by turning its weight temporarily to zero
old_weight = weights[prev]
weights[prev] = 0
try:
chosen = random.choices(population, weights)[0]
except IndexError:
# We are here because all of our weights are 0,
# which means that all is left to choose from
# is old_weight times the previous value
send_back(prev, old_weight, out)
break
out.append(chosen)
weights[chosen] -= 1
if prev is not None:
# restore weight
weights[prev] = old_weight
prev = chosen
return out
print(shuffle_without_doubles(6, 12))
[5, 1, 3, 4, 3, 2, 1, 5, 3, 5, 2, 0, 5, 4, 3, 4, 5,
3, 4, 0, 4, 1, 0, 1, 5, 3, 0, 2, 3, 4, 1, 2, 4, 1,
0, 2, 0, 2, 5, 0, 2, 1, 0, 5, 2, 0, 5, 0, 3, 2, 1,
2, 1, 5, 1, 3, 5, 4, 2, 4, 0, 4, 2, 4, 0, 1, 3, 4,
5, 3, 1, 3]
Some crude timing: it takes about 30 seconds to generate (shuffle_without_doubles(600, 1200)), so 720000 values.
I came from Creating a list without back-to-back repetitions from multiple repeating elements (referred as "problem A") as I organise my notes and there was no correct answer under "problem A" nor in the current one. Also these two problems seems different because problem A requires same elements.
Basically what you asked is same as an algorithm problem (link) where the randomness is not required. But when you have like almost half of all numbers same, the result can only be like "ABACADAEA...", where "ABCDE" are numbers. In the most voted answer to this problem, a priority queue is used so the time complexity is O(n log m), where n is the length of the output and m is the count of option.
As for this problem A easier way is to use itertools.permutations and randomly select some of them with different beginning and ending so it looks like "random"
I write draft code here and it works.
from itertools import permutations
from random import choice
def no_dup_shuffle(ele_count: int, repeat: int):
"""
Return a shuffle of `ele_count` elements repeating `repeat` times.
"""
p = permutations(range(ele_count))
res = []
curr = last = [-1] # -1 is a dummy value for the first `extend`
for _ in range(repeat):
while curr[0] == last[-1]:
curr = choice(list(p))
res.extend(curr)
last = curr
return res
def test_no_dup_shuffle(count, rep):
r = no_dup_shuffle(count, rep)
assert len(r) == count * rep # check result length
assert len(set(r)) == count # check all elements are used and in `range(count)`
for i, n in enumerate(r): # check no duplicate
assert n != r[i - 1]
print(r)
if __name__ == "__main__":
test_no_dup_shuffle(5, 3)
test_no_dup_shuffle(3, 17)
For example, the digits of 123431 and 4577852 increase and then decrease. I wrote a code that breaks the numbers into a list and is able to tell if all of the digits increase or if all of the digits decrease, but I don't know how to check for digits increasing then decreasing. How do I extend this?
x = int(input("Please enter a number: "))
y = [int(d) for d in str(x)]
def isDecreasing(y):
for i in range(len(y) - 1):
if y[i] < y[i + 1]:
return False
return True
if isDecreasing(y) == True or sorted(y) == y:
print("Yes")
Find the maximum element.
Break the list into two pieces at that location.
Check that the first piece is increasing, the second decreasing.
For your second example, 4577852, you find the largest element, 8.
Break the list in two: 4577 and 852 (the 8 can go in either list, both, or neither).
Check that 4577 is increasing (okay) and 852 is decreasing (also okay).
Is that enough to get you to a solution?
Seems like a good opportunity to learn about using itertools and generator pipelines. First we make a few simple, decoupled, and reusable components:
from itertools import tee, groupby
def digits(n):
"""420 -> 4, 2, 0"""
for char in str(n):
yield int(char)
def pairwise(iterable):
"""s -> (s0,s1), (s1,s2), (s2, s3), ..."""
a, b = tee(iterable)
next(b, None)
return zip(a, b)
def deltas(pairs):
"""2 5 3 4 -> 3, -2, 1"""
for left, right in pairs:
yield right - left
def directions(deltas):
"""3 2 2 5 6 -> -1, 0, 1, 1"""
for delta in deltas:
yield -1 if delta < 0 else +1 if delta > 0 else 0
def deduper(directions):
"""3 2 2 5 6 2 2 2 -> 3, 2, 5, 6, 2"""
for key, group in groupby(directions):
yield key
Then we put the pieces together to solve the wider problem of detecting an "increasing then decreasing number":
from itertools import zip_longest
def is_inc_dec(stream, expected=(+1, -1)):
stream = pairwise(stream)
stream = deltas(stream)
stream = directions(stream)
stream = deduper(stream)
for actual, expected in zip_longest(stream, expected):
if actual != expected or actual is None or expected is None:
return False
else:
return True
Usage is like this:
>>> stream = digits(123431)
>>> is_inc_dec(stream)
True
This solution will short-circuit correctly for a number like:
121111111111111111111111111111111111111111111111111...2
I've addressed only the "strictly increasing, and then strictly decreasing" number case. Since this sounds like it might be your homework, I'll leave it as an exercise for you to adapt the code for the "non-decreasing and then non-increasing" case which is mentioned in the question title.
Split the list at the maximum value, then take the min/ max of the diff of each side:
import numpy as np
test1 = [1, 2, 3, 4, 5, 8, 7, 3, 1, 0]
test2 = [1, 2, 3, 4, 5, 8, 7, 3, 1, 0, 2, 5]
test3 = [7, 1, 2, 3, 4, 5, 8, 7, 3, 1, 0]
test4 = [1, 2, 3, 4, 5, 8, 8, 7, 3, 1, 0]
def incdec_test(x):
i = np.array(x).argmax()
return (np.diff(x[0:i]).min() >= 0) and (np.diff(x[i:-1]).max() <= 0)
for test in [test1, test2, test3, test4]:
print 'increase then decrease = {}'.format(incdec_test(test))
Results:
increase then decrease = True
increase then decrease = False
increase then decrease = False
increase then decrease = False
Given a sorted list of increasing numbers, I'm trying to create a new list that only keeps values that are at least 3 greater than the previous number. I have tried some conditional statements, but fail to get the correct format. For example, from
a = [3,4,8,12,14,16]
we would obtain
new_a = [3,8,12,16]
Only 14 would drop out because it is less than 3 away from 12, but keep 16 because it is greater than 3 from 12. Also 4 would drop out. Any help would be appreciated!
This should do:
new_a = a[:1]
for i in a[1:]:
if i >= new_a[-1] + 3:
new_a.append(i)
a = [3,4,8,12,14,16]
b = a[:1]
last_num = b[0]
for num in a:
if last_num is not None and last_num + 3 <= num:
b.append(num)
last_num = num
print(b)
Possibly overkill, but if you end up doing this computation a lot (as well as adding new numbers to the list) you can try subclassing list.
class Newlist(list):
def __init__(self, *args):
super(Newlist, self).__init__(*args)
if len(self) > 0:
i = 1
while i<len(self):
if self.__getitem__(i) < 3+self.__getitem__(i-1):
self.remove(self.__getitem__(i))
i += 1
def append(self, item):
if len(self) == 0:
super(Newlist, self).append(item)
elif item >= self.__getitem__(-1) + 3:
super(Newlist, self).append(item)
else: return
Thus you can initialize a list with
a = Newlist([3, 4, 8, 12, 14, 16])
Which will automatically be shortened to [3, 8, 12, 16]
Also append is overriden to only allow new values that follow your rule. Example: a.append(20) will add 20 to the end of a, but a.append(17) will do nothing.