How to remove repetition when there is a common condition [duplicate] - python

This question already has answers here:
How to compare multiple variables to the same value?
(7 answers)
Closed 1 year ago.
How do i remove the repetitions
if a < 2 or b < 2 or c < 2 or d < 2:
pass #logic
I want to remove the repetitions. Something like this:
if (a,b,c,d) < 2:
pass #logic

Another slightly different way is to check if the smallest variable is less than the target value. In other words, if the smallest variable is not less than the target - then surely none of them is:
if min(a, b, c, d) < 2:
# do something
This however needs to actually find the minimal value behind the scenes and loses the short-circuiting advantage of using any with a generator expression.

I wouldn't import numpy just for this as other answers suggest. It's probably overkill.
I would do something like:
condition = lambda x: x < 2
if any(condition(x) for x in (a, b, c, d)):
# whatever you want
Or more compact (but less reusable):
if any(x < 2 for x in (a, b, c, d)):
# whatever you want

When using numpy this can be done with .all() and .any():
import numpy as np
array = np.asarray([1,2,3])
if (array < 2).any():
print('True')
else:
print('False')
#output: True
if (array < 2).all():
print('True')
else:
print('False')
#output: False

In addition to other answers, use generator expression to reduce computing time
if True in (i < 2 for i in [a,b,c,d]):
pass #logic

I would have used combination of Numpy and any here.
For example:
a, b, c, d = 10, 10, 10, 10
Convert it into a numpy array:
import numpy as np
arr = np.array([a, b, c, d])
And now you can do this:
if any(arr < 2):
pass #logic

Use built-in any function:
if any(map(lambda x: x < 2, (a, b, c, d))):
pass # whatever here

Related

Given two strings, how do I assign the shorter string to one variable and the longer string to another

I am learning python and would like to know if there is a pythonic way of doing this. Of course I can do an
if len(a) > len(b) :
(x,y) = (b,a)
and so on. But this seems a bit verbose. Is there a more beautiful way to do this in python?
Sorting seems a bit overkill. You can do it in one line just with if.
x,y = (a,b) if len(a) < len(b) else (b,a)
x = min(a, b, key=len)
y = max(a, b, key=len)
Repetition, but intention is clear.
Edit:
Removed parentheses that makes tuples out of a and b since min and max takes in variable number of arguments.
Found one way.
(x,y) = sorted([a,b], key = len)
You can use sequence unpacking with min / max. This is still O(n) complexity, albeit a 2-pass solution, which doesn't involve creating an intermediary list or tuple of strings.
a = 'hello'
b = 'test'
x, y = (func(a, b, key=len) for func in (min, max))
print(x, y, sep='\n')
# test
# hello
Here's a functional version:
from operator import methodcaller
x, y = map(methodcaller('__call__', a, b, key=len), (min, max))
Use min(n1, n2, n3, ...)
>>> n1 = "abcdefghijkl"
>>> n2 = "abc"
>>> min((n1,n2), key=len)
'abc'
>>> max((n1,n2), key=len)
'abcdefghijkl'
The question seems really opinion-based, and for this reason I think we should define beautiful referring to the Zen of Python, in particular:
Flat is better than nested.
Sparse is better than dense.
Readability counts.
For these reasons, I think that your approach:
if len(a) > len(b) :
(x,y) = (b,a)
else:
(x,y) = (a,b)
is the best one.
Just a demo how dict can also be used
Solution 1:
a = [3]
b = [2,3 ]
_bool = len(a)<len(b)
d = { True : a, False : b}
x = d[_bool]
y = d[not _bool]
print(x, y)
Using Lists:
Solution 2:
x, y = [[a, b], [b, a]][len(a) > len(b)]
Although my preferred approach is
x, y = sorted([a, b], key=len)

Converting a for loop to recursion in Python [duplicate]

This question already has answers here:
Generating permutations with repetitions
(6 answers)
Closed 3 months ago.
I am new to recursion and am trying to convert a for loop to recursion.
allProducts = []
for a in range(10):
for b in range(10):
for c in range(10):
for d in range(10):
if (a*b*c*d)%6==0:
allProducts.append(a*b*c*d)
I am not able to convert this to a recursive program, which means I am not able to scale up. The idea is this - define a recursive program in Python that takes input A (number of for loops) and B (number which is a divisor of the product).
Any help would be really helpful.
You can use itertools.product and its repeat argument:
from operator import mul
import itertools
def myprod(n, div, repeat=4):
# i is a list of factors
for i in itertools.product(range(n), repeat=repeat):
# calculate product of all elements of list
prod = reduce(mul, i, 1)
if prod % div == 0:
yield prod
print list(myprod(10, 6))
Changing the repeat argument of myprod will change the number of loops and factors you are calculating.
Also, since multiplication is commutative (a * b == b * a) you should eliminate repetitive computations using itertools.combinations_with_replacement:
from operator import mul
import itertools
def myprod_unique(n, div, repeat=4):
for i in itertools.combinations_with_replacement(range(n), r=repeat):
prod = reduce(mul, i, 1)
if prod % div == 0:
yield prod
print list(myprod_unique(10, 6))
If you remove duplicate results from myprod using set you will find that the two results are equal:
print set(myprod_unique(10, 6)) == set(myprod(10, 6))
but you have cut down the number of operations drastically from n ** r to (n+r-1)! / r! / (n-1)!. For example 92,378 instead of 10,000,000,000 for n=10, r=10.
You should have a look at the builtin package itertools:
for a,b,c,d in itertools.product(range(10),range(10),range(10),range(10)):
if (a*b*c*d)%6==0:
allProducts.append(a*b*c*d)
The other answers don't use recursion. For completeness, here is one that does.
Recursive functions usually have two parts.
Base case: Handled directly. Corresponds to the body of the innermost loop in your code.
Recursive case: Involves calling the function again, corresponds to one for loop in your code.
Sometimes extra parameters need to be introduced. In this example the variables a, b, etc. that have already been set need to be passed in. The natural way to do that is using a list.
allProducts = []
def product(vals):
res = 1
for val in vals:
res *= val
return res
# n is the number of for loops remaining
# mod is the divisor
# vals is a list where the values of a, b, c, etc. will be placed
# start this function off using run(4, 6, [])
def run(n, mod, vals):
if n == 0:
# base case
if product(vals) % mod == 0:
allProducts.append(product(vals))
else:
# recursive case
for i in range(10):
# i takes the role of a, b, c, etc.
# add i to the vals
vals.append(i)
# recursively iterate over remaining n-1 variables
run(n - 1, mod, vals)
# remove i
vals.pop()

How to check if sum of 3 integers in list matches another integer? (python)

Here's my issue:
I have a large integer (anywhere between 0 and 2^32-1). Let's call this number X.
I also have a list of integers, unsorted currently. They are all unique numbers, greater than 0 and less than X. Assume that there is a large amount of items in this list, let's say over 100,000 items.
I need to find up to 3 numbers in this list (let's call them A, B and C) that add up to X.
A, B and C all need to be inside of the list, and they can be repeated (for example, if X is 4, I can have A=1, B=1 and C=2 even though 1 would only appear once in the list).
There can be multiple solutions for A, B and C but I just need to find one possible solution for each the quickest way possible.
I've tried creating a for loop structure like this:
For A in itemlist:
For B in itemlist:
For C in itemlist:
if A + B + C == X:
exit("Done")
But since my list of integers contains over 100,000 items, this uses too much memory and would take far too long.
Is there any way to find a solution for A, B and C without using an insane amount of memory or taking an insane amount of time? Thanks in advance.
you can reduce the running time from n^3 to n^2 by using set something like that
s = set(itemlist)
for A in itemlist:
for B in itemlist:
if X-(A+B) in s:
print A,B,X-(A+B)
break
you can also sort the list and use binary search if you want to save memory
import itertools
nums = collections.Counter(itemlist)
target = t # the target sum
for i in range(len(itemlist)):
if itemlist[i] > target: continue
for j in range(i+1, len(itemlist)):
if itemlist[i]+itemlist[j] > target: continue
if target - (itemlist[i]+itemlist[j]) in nums - collections.Counter([itemlist[i], itemlist[j]]):
print("Found", itemlist[i], itemlist[j], target - (itemlist[i]+itemlist[j]))
Borrowing from #inspectorG4dget's code, this has two modifications:
If C < B then we can short-circuit the loop.
Use bisect_left() instead of collections.Counter().
This seems to run more quickly.
from random import randint
from bisect import bisect_left
X = randint(0, 2**32 - 1)
itemset = set(randint(0,X) for _ in range(100000))
itemlist = sorted(list(itemset)) # sort the list for binary search
l = len(itemlist)
for i,A in enumerate(itemlist):
for j in range(i+1, l): # use numbers above A
B = itemlist[j]
C = X - A - B # calculate C
if C <= B: continue
# see https://docs.python.org/2/library/bisect.html#searching-sorted-lists
i = bisect_left(itemlist, C)
if i != l and itemlist[i] == C:
print("Found", A, B, C)
To reduce the number of comparisons, we enforce A < B < C.

Learning Z3py - Is there support for arrays and loops

I'm going through Z3py and have a question with how to use the API in a couple specific cases. The code below is a simplified version of something I would ultimately like to use Z3 for. Some of my questions are:
1. Is there a way to create an arbitrarily long array of values like the variable 'a' below?
2. Can you access each element of the array in loops through Z3py?
3. Is it possible to assign a result back into an original variable or is a new variable needed? Will the conversion to CNF automatically add a unique id? (ie a += b)
Overall, I'm lost on how to apply the Z3py API for something like the below algorithm where the solution depends on 'b'. Any help or hints is/are appreciated, thank you.
import sys
import struct
a = "\xff\x33\x01\x88\x02\x11\x03\x55"
b = sys.stdin.read(1) #a byte of user input - value 'a' is good
c = ''
d = ''
for x in range(len(a)):
c += struct.pack( 'B', int(a[x].encode('hex'), 16)^int(b.encode('hex'), 16) )
print c.encode('hex')
second = '\x23\x23'
x = 0
while x < len(c):
d += struct.pack( 'h', int(c[x:x+1].encode('hex'), 16)^int(second.encode('hex'), 16) )
x += 2
print d.encode('hex')
if d == '\xbd\x23\x43\x23\x40\x23\x41\x23':
print "Good"
else:
print "Bad"
We can accomplish that by writing a Python program that produces a Z3 expression. We use Python loops and lists (we can also use arrays) in this program, but these lists contain Z3 "symbolic" expressions instead of Python values. The resultant list d is a list of Z3 expressions containing b. Then, we ask Z3 to find a b such that elements of d are "equal" to the characters in the string '\xbd\x23\x43\x23\x40\x23\x41\x23'. Here is the code:
from z3 import *
def str2array(a):
"""
Convert a string into a list of Z3 bit-vector values of size 8
"""
return [ BitVecVal(int(a[x].encode('hex'), 16), 8) for x in range(len(a)) ]
a = str2array("\xff\x33\x01\x88\x02\x11\x03\x55")
# 'a' is a list of Z3 bitvector constants.
print "a:", a
# The elements of 'a' may look like Python integers but they are Z3 expressions.
# We can use the method sexpr() to get these values in SMT 2.0 syntax.
print [ a_i.sexpr() for a_i in a ]
# b is a Z3 symbolic variable.
b = BitVec('b', 8)
# We compute a list 'c' of Z3 expressions from 'a' and 'b'.
# We use Python list comprehensions but we can also use a for-loop.
c = [ a_i ^ b for a_i in a ]
print "c:", c
second = '\x23\x23'
# We convert 'second' in a Z3 bit-vector value of size 16
second = BitVecVal(int(second.encode('hex'), 16), 16)
print "second:", second
# The Z3 operation Concat concatenates two or more bit-vector expressions.
d = []
x = 0
while x < len(c):
# c[x] is a Bit-vector of size 8, second is a Bit-vector of size 16.
# In Z3, we have to do the "casting" ourselves.
# We use ZeroExt(8, c[x]) to convert c[x] into a Bit-vector of size 16,
# by adding 0-bits.
d.append(ZeroExt(8, c[x]) ^ second)
x += 2
print "d:", d
goal = str2array('\xbd\x23\x43\x23\x40\x23\x41\x23')
print "goal:", goal
# Note that, len(goal) == 2*len(d)
# We can use Concat to concatenate adjacent elements.
# We want each element of d[i] == Concat(goal[2*i], goal[2*i+1])
# We can use Z3 to find the 'b' that will satisfy these constraints
s = Solver()
# 's' is a Z3 solver object
i = 0
while i < len(d):
s.add(d[i] == Concat(goal[2*i+1], goal[2*i]))
i += 1
print s
# Now, 's' contains a set of equational constraints.
print s.check()
print s.model()

Finding median without using a sort function

As a homework assignment I have to write a script which finds the median of 3 given numbers without using a standard sort function of Python.
This is my first week in class and my first programming experience so I find it very difficult to get any further than I am right now.
Here's what I have so far:
def med3(a,b,c):
list = [a, b, c]
newlist = []
if list:
minimum = list[0]
for x in list:
if x < minimum:
minimum = x
newlist.append(minimum)
list.remove(minimum)
elif x >= minimum:
newlist.append(x)
list.remove(x)
return newlist[1]
This seems to do the trick, but only for the first two entries of the list. The loop doesn't include the third entry.
How can I make the script include all three entries?
Thanks in advance!
Sander
sum([a, b, c]) - min(a, b, c) - max(a, b, c) - no sorting!
You are modifying the list in-place while looping over it, which has consequences for what elements you see:
>>> numbers = [1,2,3]
>>> for i in numbers:
... if i == 2: numbers.remove(i)
... print i
...
1
2
Note how 3 is never printed; by removing the second entry in the list, we've shortened it by one element and the loop finds the list exhausted early.
Note that you don't need to loop over the items, a few simple comparisons will tell you what item is the median if you think about it for a second. :-)
There are a number of simpler ways to go about this, but as for your approach:
You're modifying list inside of your loop. Don't do that. :)
In your case, you should be removing elements from newlist:
def med3(a,b,c):
list = [a, b, c]
newlist = []
if list:
minimum = list[0]
for x in list:
if x < minimum:
minimum = x
newlist.pop()
newlist.append(minimum)
elif x >= minimum:
newlist.append(x)
return newlist[1]
But as an exercise, you might want to think about a few things:
Why are you putting the elements in a list and looping over them? What advantage does this have over comparing a,b,c with simply if statements?
Why the if list:?
The fastest way to do it:
def medianFast(a, b, c):
if a > b:
if b > c:
return b
elif a > c:
return c
else:
return a
else:
if b < c:
return b
elif a > c:
return a
else:
return c
Guarantees you 3 comparisons at the worst case and 2 comparisons in the best case. 2,5 comparisons in average.
Using ternary conditional we can write it shorter as:
def medianTernary(a, b, c):
return (b if b > c else (c if a > c else a)) if a > b else (b if b < c else (a if a > c else c))
If you could use sorting you would have the shortest version:
def medianSorted(a, b, c):
return sorted([a, b, c])[1]

Categories

Resources