permutations with restrictions on python - python

I want to create all possible sequence of numbers with 10 digits by using numbers 0 to 9 . Each digit should be used only once like:
5 2 8 7 3 0 6 1 9 4 and 5 0 6 2 7 8 3 1 9 4.
So there are P(10,10) = 3 628 800 possible combinations out there. However there are three rules about the ordering of elements:
2 > 8 > 3 > 9 > 4 (rule 1) and 5 > 9 > 4 (rule 2) and 2 > 8 > 1 (rule 3). Here > sign means left operand should be on a higher significance digit (meaning closer to the left). Unspecified numbers in the rules which are 0 6 7 could be used anywhere on the combination.
So I have this idea of having a list of all possible combinations and then applying rules one by one and discard the sequences that do not follow. However, after some thoughts, found out that I can further assess the precise position for some digits, lets say 2. Looking at 2,8,3,9,4 (rule 1), there should be at least 4 elements on the rightside of 2--> (8,3,9,4). This makes 2 could be assigned to digits D(1,2,3,4,5,6) (1 is MSB and 10 is LSB). Further investigating 2,8,1 (rule 3) 1 should also be on the right side of 2 that makes 2 could be assigned to D(1,2,3,4,5). There are no further simplifications I figured out, and that's it I guess.
So what kind of approach might work here? I don't know even if the brute-force approach I suggest is feasible yet have to check it but I'm looking for a neater pythonic way to implement this.
P.S. An idea is that we can split rules like
(2>8),(2>3),(2>9),(2>4)
(8>3),(8>9),(8>4)
(3>9),(3>4)
(9>4) for rule 1. But it still looks like a brute-force to me.

Easy, you can use itertools.permutations(), and as it generates tuples, we check to see each tuple meets the rules:
from itertools import permutations
for t in permutations(range(10), 10):
if t.index(2) < t.index(8) < t.index(3) < t.index(9) < t.index(4) and t.index(5) < t.index(9) < t.index(4) and t.index(2) < t.index(8) < t.index(1):
print(t)

For the brute force method & for rule1 and rule2, you can do this:
import itertools
allpermutations = list(itertools.permutations([x for x in range(10)]))
def rule1(seq):
return seq.index(2) < seq.index(8) and seq.index(8) < seq.index(3) and seq.index(3) < seq.index(9) and seq.index(9) < seq.index(4)
def rule2(seq):
return seq.index(5) < seq.index(9) and seq.index(9) < seq.index(4)
result = [eachsequence for eachsequence in allpermutations if rule1(eachsequence) and rule2(eachsequence)]
(Similarly for rule3.)
For itertools usage, see this, for the .index() method, this.

Related

Get unique groups from a list

I am trying to create an algoritm to create unique groups of length = N from a list L with len(L) values.
EDIT: Unique group is the one, where any of the values has never been with any of the values in a group before.
EDIT: So if instead of values we would have people, anyone in the group should always meet only new people in the new group.
Say we have a list L and try to find unique groups of 4:
L = [1,2,3,4,5,6,7,8]
N = 4
unique_groups = [[1,2,3,4], [5,6,7,8]]
len(unique_groups) = 2
So here we have 8 values and 2 unique groups, any new group would contain at least one value which is contained in previous one, e.g. [1,2,3,5] or [1,3,5,7] contain at least two values from before, so these groups are not unique.
Where len(L) = 12, we have 3 different groups, while len(L) >= 16 gives us way more options:
L = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
N = 4
unique_groups = [[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16], [1,5,9,13], [2,3,7,12] ...]
len(unique_groups) = ?
I have tried few unintuitive and slow approaches, namely comparing all combinations, which should take a lot of time if length of list gets bigger.
This is one of the approaches:
def findsubsets(s, n):
return list(itertools.combinations(s, n))
s = [1,2,3,4,5,6,7,8]
sets = findsubsets(s,4)
sets_unique = []
def compare_sets(set1, set2):
init_eq = 0
for s1 in set1:
if s1 in set2 and init_eq > 0:
return False
elif s1 in set2 and init_eq < 1:
init_eq += 1
else:
continue
return True
for s in sets:
start_point = sets.index(s)
print(start_point)
for i in range(start_point + 1, len(sets) + 1):
set2 = sets[i]
if compare_sets(s, set2):
print(s, set2)
sets_unique.append(set2)
print(sets_unique)
EDIT2: Real life problem of this, is to match employees in a group of N, so these groups never contain two same people. Every person should meet only new people.
Your question update is quite clear now; thanks.
This problem is isomorphic to sets of points and lines in a projective plane. You are trying to construct as many lines as you can with N points on each line. Look under the subsection "A finite example" for a visualization of the process, and "Vector space construction" for the formal algorithm.
To give you an idea here you will begin at an arbitrarily chosen point1. Make sets (colinear points) by appending (conveniently) consecutive, disjoint triples:
1 2 3 4
1 5 6 7
1 8 9 10
1 11 12 13
...
This gives you all groups containing point 1. Now comes the mathematically interesting part: how you cross-link the points 2 and higher will define a projective plane; the solution from here is not unique. One standard algorithm searches for a homeomorphic solution, a greedy algorithm: at each choice point, choose the lowest numbered point that is legal for the current open slot.
This will give us
2 5 8 11
2 6 9 12
2 7 10 13
...
3 5 9 13
3 6 10 11
et cetera
You will need to define whether you want all possible distinct solutions, or merely the first one you can find. Each distinct solution defines a different projective plane topology.
Dealing with this from a standpoint of projective planes has you start with affines and derive the planes from there. Esepcially check out the three properties of an affine plane
Additional references:
http://www.mathpuzzle.com/MAA/47-Fano/mathgames_05_30_06.html
Does that give you enough to play with for now?

loop numbers down and up and not using recursion in python

I have a small problem with my code. I can't seem to figure out how to do this. I can get it to work with two for loops. But in the exercise it says that i only can use one loop to get the result.
The code is supposed to execute this:
bounce2(4):
4
3
2
1
0
1
2
3
4
What I have come up with:
def bounce2(n):
for x in range(n,-1,-1):
print(x)
Which prints out 4,3,2,1,0
But now i dont know what to do..
I have tried different if statements such as:
def bounce2(n):
for x in range(n,-1,-1):
print(x)
if n == 0:
x = x + 1
print(x)
But they only print one integer because they are out of the loop.
Same thing goes if i try to make the if-statement inside the loop, then it prints out something like 433221100. I dont know how to get the numbers to switch places. The print statement should also be an integer and not a string. So i can't use replaced.
Really need help to figure out the logic. All help is appreciated.
So, a little bit of my thought process before showing you the code. Clearly there are nine lines, or more generally n * 2 + 1 lines. Because we need to count down to 0 and back up. That's how many times you need to call print.
Now, if you add line numbers to the expected output and think of it as a table describing a function f(i, n) where i is the line number, and n is the starting and ending value. what is f? Can you write down the formula? e.g.
i f(i, 4)
0 4
1 3
2 2
3 1
4 0
5 1
6 2
7 3
8 4
We can write down the basic structure of the code, we still don't know what f look like but assume we have it:
for i in range(2*n+1):
print f(i)
And, what is f? Now you need to be a little creative and maybe experiment a bit. What I did was to try basic arithmetic combinations of i and n to match f(i, n), and I quickly noticed that n - i works until we reach the second half of the output, which only differs by a - sign.
i f(i, 4) n - i
0 4 4
1 3 3
2 2 2
3 1 1
4 0 0
5 1 -1
6 2 -2
7 3 -3
8 4 -4
Soooo, take the absolute value of n - i or i - n, whatever.
def f(i, n):
return abs(n-i)
Here is what I believe to be a pretty elegant solution:
def bounce(n):
for x in range(-n, n+1):
print(abs(x))
Our loop goes from the negative of n to the positive of n, printing the absolute value.
Since you need to count n times downwards, and another n times upwards, and 1 comes from counting 0, instead of actually counting downwards and then upwards in two separate loops, we can use one loop to count upwards 2 * n + 1 times, which effectively is like counting towards n and then bouncing off n, so we can simply calculate the "distance" to n instead, which is the absolute value of n - x:
def bounce2(n):
for x in range(2 * n + 1):
print(abs(n - x))
so that bounce2(4) would output:
4
3
2
1
0
1
2
3
4
a very simple solution will be:
for i in range(n, -(n+1), -1):
print(abs(i))
this like mirroring numbers around some point.
in your case that point is zero and to have identical mirroring use abs
Try the below, have a list l with a element of str(n) iterate trough the range of n times 2, then check x is bigger than n+2 if it is add 1 to n, otherwise subtract 1 from n, both cases append to l, then at the end, do str.join to join '\n' (newline) with l:
def bounce2(n):
l=[str(n)]
for x in range(n*2):
if x>n+2:
n+=1
l.append(str(n))
else:
n-=1
l.append(str(n))
return '\n'.join(l)
print(bounce2(4))
Output:
4
3
2
1
0
1
2
3
4

Need explanation of how function works printing digits in reverse

I cheated after giving up of how to figure out how to print digits backwards making a function for it but I still do not quite understand how it works. For instance why does it print the digits backwards and not in order?
def print_digits(n):
"""
>>> print_digits(13789)
9 8 7 3 1
>>> print_digits(39874613)
3 1 6 4 7 8 9 3
>>> print_digits(213141)
1 4 1 3 1 2
"""
while n > 0:
print n % 10
n = n / 10
I would appreciate a line by line explanation starting with the while loop. I've tried tracing it in my head and on paper but just can't grasp the code in the function.
In the first line in the loop the '%' operator devides the number given by 10 and returns the rest only, means the fraction of the division (25 : 10 = 2.5, so it returns the 5 only!).
The line 'n/10' then does exactly the other way around and stores part left of the comma into the variable itself, as the '/' operator returns only the left part of the comma.
In short you can say:
n%10 returns only the rest of the divison
n/10 "throws" the rest of the division away
the code repeats
% operator returns the remainder of division. (20%3=2,24%5=4).
When you divide a number by 10 remainder is always the last digit.
For example 123/10=12 & remainder is 3. So 123%10=3.
Inside the while loop while n is greater than 0 the last digit of n is printed.
And because of the line n=n/10, n becomes n/10. Here integer division has used so finally value of n will become 0 and then the loop will stop.( if n is initially 123 value of n will change as 123,12,1,0 and then loop will stop.)

Getting the number of digits of nonnegative integers (Python) [duplicate]

This question already has answers here:
How to find length of digits in an integer?
(31 answers)
Closed 6 years ago.
The question asks:
<< BACKGROUND STORY:
Suppose we’re designing a point-of-sale and order-tracking system for a new burger
joint. It is a small joint and it only sells 4 options for combos: Classic Single
Combo (hamburger with one patty), Classic Double With Cheese Combo (2 patties),
and Classic Triple with Cheese Combo (3 patties), Avant-Garde Quadruple with
Guacamole Combo (4 patties). We shall encode these combos as 1, 2, 3, and 4
respectively. Each meal can be biggie sized to acquire a larger box of fries and
drink. A biggie sized combo is represented by 5, 6, 7, and 8 respectively, for the
combos 1, 2, 3, and 4 respectively. >>
Write an iterative function called order_size which takes an order and returns the number of combos in the order. For example, order_size(237) -> 3.
Whereby I should have
order_size(0) = 0
order_size(6) = 1
order_size(51) = 2
order_size(682) = 3
My code is:
def order_size(order):
# Fill in your code here
if order > 0:
size = 0
while order > 0:
size += 1
order = order // 10
return size
else:
return 0
But I don't get the order // 10 portion. I'm guessing it's wrong but I can't think of any stuff to substitute that.
No need for iterative function, you can measure the length of the number by "turning" it into a string:
num = 127
order = len(str(num))
print(order) # prints 3
But if you really want to do it iteratively:
def order(num):
res = 0
while num > 0:
num = int(num / 10)
res += 1
return res
print(order(127)) # prints 3
How about this:
from math import log
def order_size(order):
if order <= 0: return 0
return int(log(order, 10) + 1)
Some samples (left column order, right column order size):
0 0
5 1
10 2
15 2
20 2
100 3
893 3
10232 5
There are a couple errors in your suggested answer.
The else statement and both return statements should be indented a level less.
Your tester questions indicate you are supposed to count the digits for nonnegative integers, not just positive ones (i.e. you algorithm must work on 0).
Here is my suggested alternative based on yours and the criteria of the task.
def order_size(order):
# Fill in your code here
if order >= 0:
size = 0
while order > 0:
size += 1
order = order // 10
return size
else:
return 0
Notice that
By using an inclusive inequality in the if condition, I am allowing 0 to enter the while loop, as I would any other nonnegative single digit number.
By pushing the first return statement back, it executes after the while loop. Thus after the order is counted in the variable size, it is returned.
By pushing the else: back, it executes in the even the if condition is not met (i.e. when the numbers passed to order_size(n) is negative).
By pushing the second return back, it is syntactically correct, and contained in the else block, as it should be.
Now that's taken care of, let me address this:
But I don't get the order // 10 portion.
As of Python 3, the // is a floor division (a.k.a integer division) binary operation.
It effectively performs a standard division, then rounds down (towards negative infinity) to the nearest integer.
Here are some examples to help you out. Pay attention to the last one especially.
10 // 2 # Returns 5 since 10/2 = 5, rounded down is 5
2 // 2 # Returns 1 since 2/2 = 1, rounded down is 1
11 // 2 # Returns 5 since 11/2 = 5.5, rounded down is 5
4 // 10 # Returns 0 since 4/10 = 0.4, rounded down is 0
(-4) // 10 # Returns -1 since (-4)/10 = -0.4, rounded down is -1
For nonnegative numerator n, n // d can be seen as the number of times d fits into n whole.
So for a number like n = 1042, n // 10 would give you how many whole times 10 fits into 1042.
This is 104 (since 1042/10 = 104.2, and rounded down we have 104).
Notice how we've effectively knocked off a digit?
Let's have a look at your while loop.
while order > 0:
size += 1
order = order // 10
Every time a digit is "knocked off" order, the size counter is incremented, thus counting how many digits you can knock off before you hit your terminating step.
Termination occurs when you knock of the final (single) digit. For example, say you reduced order to 1 (from 1042), then 1 // 10 returns 0.
So once all the digits are "knocked off" and counted, your order will have a value of 0. The while loop will then terminate, and your size counter will be returned.
Hope this helps!
Disclaimer: Perhaps this isn't what you want to hear, but many Universities consider copying code from the Internet and passing it off as your own to be plagiarism.

Checking to see if a number ends in 5?

I'm trying to define a function that takes 2 parameters, adds them up, and if the sum of the two parameters ends in 5, it reports a 2. If it doesn't end in 5, it returns 8.
Any ideas?
I was thinking of doing an if statement, but I'm confused as to how I would check if a number ends in 5( or is 5).
Thanks for your help, trying to teach myself how to program is so difficult yet so rewarding :)
Solution
My answer assumes you are checking integers (which seems pretty reasonable judging from your question):
def sum_ends_with_5(a, b):
"""
Checks if sum ends with "5" digit.
"""
result = a + b
return 2 if result % 10 == 5 else 8
or more flexible (with any number of arguments):
def sum_ends_with_5(*args):
"""
Checks if sum ends with "5" digit.
"""
result = sum(args)
return 2 if result % 10 == 5 else 8
How it works (aka tests)
The function behaves like that:
>>> sum_ends_with_5(5)
2
>>> sum_ends_with_5(3)
8
>>> sum_ends_with_5(2, 8)
8
>>> sum_ends_with_5(7, 8)
2
>>> sum_ends_with_5(10, 20, 3, 2)
2
Shorter version
So, if you want to write it in shorter and more flexible way, you can do this:
def sum_ends_with_5(*args):
return 2 if sum(args) % 10 == 5 else 8
Take the modulus by 10 and check if it's 5.
print num % 10 == 5
Numbers end in 5 if and only if they are are divisible by 5 but are not divisible by 10. You can easily check for these conditions with modulo arithmetic. More generally, you can check if a number ends with a digit by comparing the mod 10 value of that number to the digit.
num = 1234
isDivisibleByFive = num % 10 == 5
One easy approach is to take the number and convert it to a string and check the last digit using indexing to see if it is 5:
E.g.,
n = 153
str(n)[-1] == '5':
False
and
n = 155
str(155)[-1] == '5'
True
So as part of an if-statement:
if str(n)[-1] == `5`:
print "number ends in 5"
else:
print "number did not end in 5"
If you just wanted to check for divisibility by 5 (which is different than ending with 5) you could use the mod operation.
But you also could mod by 10 and check for a remainder of 5 to determine if the number (int) ends with 5. My solution checks for the last digit of any number (including floats)
I like the solution from Tadeck best but there is another way, not as good in my opinion for this specific use case, but still may be useful if your return values ever need to follow more complex rules than is available from a simple modulo operation.
def xlatVal (*nums):
# 0 1 2 3 4 5 6 7 8 9
lookupTbl = [8,8,8,8,8,2,8,8,8,8]
return lookupTbl[sum(nums) % 10]
While the values are still reduced to a range using modulo, this allows arbitrary translations across that range.
Convert it to a string and check the last character:
str(num)[-1] == "5"

Categories