Possible orderings of points of a pair of segments - python

Suppose a, b, c, and d hold floating point numbers. Suppose it must hold that a <= b and c <= d. Given these constraints I am trying to find all of the possible orderings using only <, and =. For example a < c = b < d is one possible ordering. b < c < a < d is not, due to the constraint that a <= b.

If you only allow < or = as operators, then the valid expressions will have the following properties
as the constraints only use ≤, whether the expressions use < or = does not matter
the constraints mean a must be before b and c must be before d
You can either generate all permutations and prune those which don't have a before b and c before d in them out, or you can generate based on those constraints, by deducing:
the first variable must be a or c
the second variable can either be the second variable in the constraint of the first, or other option for first
if the second is the same constraint as the first, then the other two must be the other two in order ( [a b c d] or [c d a b] )
if the variables for the constraints are interleaved, the third and forth can be in either order
That should give:
a∙b∙c∙d
a∙c∙b∙d
a∙c∙d∙b
c∙d∙a∙b
c∙a∙d∙b
c∙a∙b∙d
where ∙ can be either < or = in any of the positions.
If you also allow > as an operator, you get some which are always valid and some which can be either valid or not.
a∙b>c∙d
c∙d>a∙b
are valid as the > order doesn't occur between any of the variables that are paired in the constraints. I think any other cases with > it breaks the transitivity so you can't know whether the result obeys the constraints or not.
from itertools import zip_longest
def interleave_strings(a,b):
return ''.join(''.join(x) for x in zip_longest(a, b, fillvalue=''))
VARIABLE_ORDERINGS = ['abcd','acbd','cdab','cadb','cabd']
OPERATOR_COMBINATIONS = ['<<<','=<<','<=<','<<=','<==','=<=','==<','===']
for vars in VARIABLE_ORDERINGS:
for ops in OPERATOR_COMBINATIONS:
# only include a=b not b=a to elide equivalent expressions
if ops[0] == '=' and vars[0] > vars[1]: continue
if ops[1] == '=' and vars[1] > vars[2]: continue
if ops[2] == '=' and vars[2] > vars[3]: continue
print(interleave_strings(vars, ops))

Related

Use z3Py to prove the equivalence/difference of ranges of two expressions

from z3 import *
s = Solver()
a, b = Ints("a b")
s.add(a > 2)
s.add(b > 0)
s.add(Or(Exists(a, ForAll(b, a != b)), Exists(b, ForAll(a, a != b))))
s.check() # print "unsat"
I am trying to prove the difference of ranges of a and b. This can be done by locating an assignment to b of value 1 which is beyond the range of a.
However, the program above gives unexpected unsat. I wonder why and whether there is more efficient way to achieve this goal.
ForAll means exactly that: all numbers, i.e. Exists(a, ForAll(b, a != b)) is always false because there is no Int that's different from all Ints and thus the third assertion is unsatisfiable all by itself. You want something like s.add(Exists(a, (Exists (b, And(Not(a > 2), b > 0))))).
Also, note that you use two different a and b. Exists(a, ...) does not quantify over an existing variable, but introduces a new variable that's accidentally called by the same name as your global (existential) a, b.

how do you write out "A is greater than B, C, and D" in an efficient matter?

How can I make this statement shorter?
A simple solution is to combine B, C and D into one "value" using max, so you only perform one explicit test. For example, instead of:
if A > B and A > C and A > D:
just write:
if A > max(B, C, D):
An alternative but functionally similar approach would be to use all with a generator expression:
if all(A > x for x in (B, C, D)):
In theory, the generator expression approach is "faster", in that it performs fewer total comparisons and can short-circuit. That said, generator expressions have some overhead in terms of per loop Python byte code execution, so odds are the two perform similarly in practice, unless the objects in question have very expensive implementations of __gt__ (the overload for >).
Note that if you don't need a single unique maximum, a slightly different behavior, but with far fewer tests, could be performed:
maxval = max(A, B, C, D)
if maxval is A:
...
elif maxval is B:
...
elif maxval is C:
...
elif maxval is D:
...
This does differ behaviorally though; unlike the other code, a tie here is resolved semi-arbitrarily with only one element "winning", where the code you describe would treat a tie as "no winner". Replacing is with == and elif with if would treat a tie as "all tied for max are winners". It all depends on your desired behavior.

What do operator //= and :: do?

I am working on the Euler problems and found this code online for the third problem.
I used Python 3 to solve the third and fourth problems.
def lpf(c): # largest_prime_factor
i = 2
while i * i <= c:
if c % i:
i += 1
else:
c //= i # c // i returns the integer quotient of c/i
return c
I know that the // returns the integer quotient of a division, but what is happening when an equal sign is put just after it?
c //= i --> Is the integer quotient of c/i affected to the variable c?
Also, I am working on palindromes for problem 4 and I found this operator ::.
For example, here is code using it:
> s = str(12321)
> print(s == s[::-1])
Output : True
(If the number is a palindrome, then output is True, else the output is False.)
Is the operator :: reading the string and changing it with an option? If so, how do you use it?
If you read an operator like ??= with ?? a special operator, that usually (not always), can be replaced by:
c ??= e
into:
c = c ?? e
So the operation is done inplace. So here you have written:
c = c // i # equivalent to c //= i
Note that sometimes inplace operations differ from their non-inplace counterparts. For instance for a list la += lb is not equal to la = la + lb. Since numerical values are immutable however, there is not much we can optimize here, so for numbers the above will (probably) always hold.

Shorthand code and Fibonnaci Sequence [duplicate]

This question already has answers here:
Multiple assignment and evaluation order in Python
(11 answers)
Closed 7 years ago.
I have found example for Fibonacci sequence that goes like this:
def fib(n):
a, b = 0, 1
while b < n:
print (b)
a, b = b, a+b
fib(20)
So here's what I don't get it:
a, b = 0, 1 # is just a shortcut for writing
a = 0
b = 1
right?
Now, following the same logic
a, b = b, a+b #should be the same as writing
a = b
b = a+b
But it isn't because if I write it like that, the output is different.
I'm having some hard time understanding why. Any thoughts?
Yes It isn't exactly the same , because when you write -
a, b = b, a+b
The value of a and b at the time of executing the statement is considered, lets say before this statement, a=1 , b=2 , then first right hand side is calculated , so b=2 is calculated and a+b=3 is calculated. Then the assignment occurs, that is a is assigned value 2 and b is assigned value 3.
But when you write -
a = b
b = a+b
The assignment occurs along with calculation, that is first b=2 is calculated, then assigned to a , so a becomes 2 , then a+b is calculated (with the changed value of a) , so a+b=4 and it is assigned to b , so b becomes 4 , and hence the difference.
a,b = b, a
This is a shorthand for swapping values of a and b , please note that if you want to swap the values without using this notation, you would need a temporary variable.
Internally how it works is that the right hand sid is made into a tuple, and then the values are unpacked, a simple test to see this is -
>>> a = 5
>>> b = 10
>>> t = a, b
>>> t
(5, 10)
>>> b, a = t
a, b = c, d is not shorthand for the following:
a = c
b = d
It's actually shorthand for this:
a, b = (c, d)
I.e., you're creating a tuple (c, d), a tuple with the values of c and d, which is then unpacked into the target list a, b. The tuple with its values is created before the values are unpacked into the target list. It actually is one atomic* operation, not a shorthand for several operations. So it does not matter whether one of the variables in the target list also occurs on the right hand side of the assignment operation.
* Not "atomic" in the sense of database ACID, but still not separate statements.
It is not the same thing.
x, y = y, x
equals to:
t = x
x = y
y = t
It actually uses a temporary variable to swap x and y.
So back to a, b = b, a+b. This expression equals to:
m = a; n = b
a = n
b = m + n

Splitting a string in Python to 3 segments appointed to 3 variables depending on item count

I'm new to Python and struggling to solve the following issue the most Pythonic way.
I have a string (Example states given below) which needs to be split (.split('/', 2)) and appointed (up) to 3 variables (vars. a, b and c). The string is a URL which I need to split into 3 segments.
The string and its segments can be the following examples:
'seg_a/seb_b/the_rest' -> a = seg_a, b = seg_b, c = the_rest
'seg_a/the_rest' -> a = seg_a, b = None, c = the_rest
'seg_a' -> a = seg_a, b = None, c = None
Note: No obligation exists to have None value given if nothing else gets appointed. They simple may not exist (b in ex. 2, b and c in ex. 3).
If split results in 1 item, it's given to variable a.
If split results in 2 items, it's given to variable a and c
If split results in 3 items, then it's segments are given to variables a, b and c
I have found 2 methods achieving this, both seem not Pythonic, hence resulting in this question.
Method A:
Split.
Count.
Depending on count, appoint segments to variables with IF.. Elif.. Elif.. Else. statement
Method B:
Use list comprehension and nested Try-Except blocks. Ex:
try:
a, b, c = [i for i in to_split.split("/", 2)]
except ValueError:
try:
a, c = [i for i in to_split.split("/", 1)]
b = None
except ValueError:
a = to_split
b, c = None, None
My question (short):
What is the correct, Pythonic way of splitting this string to its
segments and appointing them to variables a, b and c?
I would do:
l = to_split.split("/", 2)
a, b, c = l + [None] * (3 - len(l))
IMHO, what is most Pythonic isn't what's most clever. If something is simple, concise, and comprehensible at a glance, then use it and get on with your day. If the rules you want to impose are
If split results in 1 item, it's given to variable a.
If split results in 2 items, it's given to variables a and c.
If split results in 3 items, it's given to variables a, b and c.
Then just implement that, Method A-style.
p = to_split.split("/", 2)
if len(p) == 1:
a, = p
elif len(p) == 2:
a, c = p
elif len(p) == 3:
a, b, c = p
else:
raise ValueError("could not parse {}".format(to_split))
I can read this and know exactly what it's doing. If there's a bug in there -- say I've swapped b and c when len(p) == 2 -- it's easy to fix once I see the problem.
It does seem a little strange that you're willing to let variables be undefined -- you must branch later to avoid getting a NameError, and that could, and probably should, be avoided with some refactoring. In my experience, something is probably a little off elsewhere. Even without changing anything else, I'd include a, b, c = [None]*3, myself.
One rule which helps keep code maintainable is that we should try to minimize the distance between what we would tell someone an algorithm is supposed to do and how we told the computer what to do. Here, since what you want to do is almost transcribable directly into Python, I'd just do that.
You could try:
a,b,c = (to_split("/",2) + [None]*3)[0:3]
However I agree with #DSM: the most pythonic way is not always the best approach to solve a problem. It could be ok at first, but a more verbose code works best in terms of readability.
That's one of the reasons I love Python: there are several ways to solve a problem, and it's up to the developer to choose the best according to his/her needs.

Categories