I have 3 lists, the first of which is a list of 5 random digits, as shown below:
import random
def rollDice():
dice = []
for i in range(5):
dice.append(random.randint(1,6))
return sorted(dice)
dice = rollDice()
largeStraight = [[1,2,3,4,5] , [2,3,4,5,6]]
smallStraight = [[1,2,3,4] , [2,3,4,5] , [3,4,5,6]]
My question is what is the best way to see if dice is equal to either of the nested lists in largeStraight, and secondly whether any of the nested lists in smallStraight are a subset of dice. I am looking for a simple true or false return.
Thanks for any help.
Use sets instead of lists:
largeStraight = [{1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}]
smallStraight = [{1, 2, 3, 4}, {2, 3, 4, 5} , {3 ,4, 5, 6}]
Now you can use set operations:
if any(ls.issubset(dice) for ls in largeStraight):
# a large straight
elif any(ss.issubset(dice) for ss in smallStraight):
# a small straight
You can still turn each list in largeStraight and smallStraight into a set in the generator expression passed to any(), but that'd be a waste of CPU cycles.
Demo:
>>> dice = [2, 3, 5, 1, 4]
>>> if any(ls.issubset(dice) for ls in largeStraight):
... print 'Large!'
... elif any(ss.issubset(dice) for ss in smallStraight):
... print 'Small!'
...
Large!
>>> dice = [2, 3, 5, 1, 6]
>>> if any(ls.issubset(dice) for ls in largeStraight):
... print 'Large!'
... elif any(ss.issubset(dice) for ss in smallStraight):
... print 'Small!'
...
>>> dice = [2, 3, 6, 4, 1]
>>> if any(ls.issubset(dice) for ls in largeStraight):
... print 'Large!'
... elif any(ss.issubset(dice) for ss in smallStraight):
... print 'Small!'
...
Small!
If order is important, for the large straight, you could simply do this:
dice in largeStraight
and for the small straight, you could do this:
any(i in (dice[0:4], dice[1:5]) for i in smallStraight)
Alternatively, you could replace the rollDice function with this:
def rollDice():
dice = set()
for i in range(5):
dice.add(random.randint(1, 6))
return dice
and use sets as suggested in other answers.
You could also replace your current rollDice definition with a list comprehension:
def rollDice():
return sorted([random.randint(1, 6) for _ in range(5)])
or
def rollDice():
return {random.randint(1, 6) for _ in range(5)}
for a set.
However, I'd advise against using sets. It would work in this case, but I presume that this is part of a larger program. Sets can't have duplicate elements, so if you later wanted to check if there was a pair of equal numbers in dice, and it was a set, then you would always get a negative response.
Here is possible solution
is_large_straight = any(set(x) & set(dice) == set(x) for x in largeStraight)
is_small_straight = any(set(x) & set(dice) == set(x) for x in smallStraight)
I hope it'll help you.
Related
I am trying to achieve this without the use of external libraries. If anyone could help me out with this and include a bit of an explanation that would be greatly appreciated.
def mode(list):
dictionary= {}
for i in list:
dictionary.setdefault(i, 0)
dictionary[i] += 1
maximum = max(dictionary,key=dictionary.get)
Here's a naive (non-optimal but very simple) solution using max and count:
>>> def mode(nums):
... return max(nums, key=nums.count)
...
>>> mode([1, 2, 3, 3, 4])
3
The more efficient solution (because it doesn't require iterating over nums once per item in nums) is indeed to use a counter. Your code mostly does this, but doesn't actually return the result:
>>> def mode(nums):
... counter = {}
... for i in nums:
... counter[i] = counter.get(i, 0) + 1
... return max(counter, key=counter.get)
...
>>> mode([1, 2, 3, 3, 4])
3
Note that the statistics module is built into Python (i.e. it is not an external library) and it contains a mode function:
>>> from statistics import mode
>>> mode([1, 2, 3, 3, 4])
3
I would like to know if there exists a base solution to do something like this:
for n in range(length=8, start_position= 3, direction= forward)
The problem I'm encountering is I would like the loop to continue past the final index, and pick up again at idx =0, then idx=1, etc. and stop at idx= 3, the start_position.
To give context, I seek all possible complete solutions to the n-queen problem.
Based on your latest edit, you need a "normal" range and the modulo operator:
for i in range(START, START + LEN):
do_something_with(i % LEN)
from itertools import chain
for n in chain(range(3,8), range(3)):
...
The chain() returns an iterator with 3, 4, ..., 7, 0, 1, 2
Another option for solving this is to use modular arithmetic. You could do something like this, for example:
for i in range(8)
idx = (i + 3) % 8
# use idx
This easily can be generalized to work with different lengths and offsets.
def loop_around_range(length, start_position, direction='forward'):
looped_range = [k % length for k in range(start_position, start_position+length)]
if direction == 'forward':
return looped_range
else:
return looped_range[::-1]
You could implement this for an arbitrary iterable by using itertools.cycle.
from itertools import cycle
def circular_iterator(iterable, skip=0, length=None, reverse=False):
"""Produces a full cycle of #iterable#, skipping the first #skip# elements
then tacking them on to the end.
if #iterable# does not implement #__len__#, you must provide #length#
"""
if reverse:
iterable = reversed(iterable)
cyc_iter = cycle(iterable)
for _ in range(skip):
next(cyc_iter, None)
if length:
total_length = length
else:
total_length = len(iterable)
for _ in range(total_length):
yield next(cyc_iter, None)
>>> lst = [x for x in range(1, 9)]
# [1, 2, 3, 4, 5, 6, 7, 8]
>>> list(circular_iterator(lst, skip=3))
[4, 5, 6, 7, 8, 1, 2, 3]
Hi so I'm trying to do the following but have gotten a bit stuck. Say I have a list of sets:
A = [set([1,2]), set([3,4]), set([1,6]), set([1,5])]
I want to create a new list which looks like the following:
B = [ set([1,2,5,6]), set([3,4]) ]
i.e create a list of sets with the sets joined if they overlap. This is probably simple but I can't quite get it right this morning.
This also works and is quite short:
import itertools
groups = [{'1', '2'}, {'3', '2'}, {'2', '4'}, {'5', '6'}, {'7', '8'}, {'7','9'}]
while True:
for s1, s2 in itertools.combinations(groups, 2):
if s1.intersection(s2):
break
else:
break
groups.remove(s1)
groups.remove(s2)
groups.append(s1.union(s2))
groups
This gives the following output:
[{'5', '6'}, {'1', '2', '3', '4'}, {'7', '8', '9'}]
The while True does seems a bit dangerous to me, any thoughts anyone?
How about:
from collections import defaultdict
def sortOverlap(listOfTuples):
# The locations of the values
locations = defaultdict(lambda: [])
# 'Sorted' list to return
sortedList = []
# For each tuple in the original list
for i, a in enumerate(listOfTuples):
for k, element in enumerate(a):
locations[element].append(i)
# Now construct the sorted list
coveredElements = set()
for element, tupleIndices in locations.iteritems():
# If we've seen this element already then skip it
if element in coveredElements:
continue
# Combine the lists
temp = []
for index in tupleIndices:
temp += listOfTuples[index]
# Add to the list of sorted tuples
sortedList.append(list(set(temp)))
# Record that we've covered this element
for element in sortedList[-1]:
coveredElements.add(element)
return sortedList
# Run the example (with tuples)
print sortOverlap([(1,2), (3,4), (1,5), (1,6)])
# Run the example (with sets)
print sortOverlap([set([1,2]), set([3,4]), set([1,5]), set([1,6])])
You could use intersection() and union() in for loops:
A = [set([1,2]), set([3,4]), set([1,6]), set([1,5])]
intersecting = []
for someSet in A:
for anotherSet in A:
if someSet.intersection(anotherSet) and someSet != anotherSet:
intersecting.append(someSet.union(anotherSet))
A.pop(A.index(anotherSet))
A.pop(A.index(someSet))
finalSet = set([])
for someSet in intersecting:
finalSet = finalSet.union(someSet)
A.append(finalSet)
print A
Output: [set([3, 4]), set([1, 2, 5, 6])]
A slightly more straightforward solution,
def overlaps(sets):
overlapping = []
for a in sets:
match = False
for b in overlapping:
if a.intersection(b):
b.update(a)
match = True
break
if not match:
overlapping.append(a)
return overlapping
examples
>>> overlaps([set([1,2]), set([1,3]), set([1,6]), set([3,5])])
[{1, 2, 3, 5, 6}]
>>> overlaps([set([1,2]), set([3,4]), set([1,6]), set([1,5])])
[{1, 2, 5, 6}, {3, 4}]
for set_ in A:
new_set = set(set_)
for other_set in A:
if other_set == new_set:
continue
for item in other_set:
if item in set_:
new_set = new_set.union(other_set)
break
if new_set not in B:
B.append(new_set)
Input/Output:
A = [set([1,2]), set([3,4]), set([2,3]) ]
B = [set([1, 2, 3]), set([2, 3, 4]), set([1, 2, 3, 4])]
A = [set([1,2]), set([3,4]), set([1,6]), set([1,5])]
B = [set([1, 2, 5, 6]), set([3, 4])]
A = [set([1,2]), set([1,3]), set([1,6]), set([3,5])]
B = [set([1, 2, 3, 6]), set([1, 2, 3, 5, 6]), set([1, 3, 5])]
This function will do the job, without touching the input:
from copy import deepcopy
def remove_overlapped(input_list):
input = deepcopy(input_list)
output = []
index = 1
while input:
head = input[0]
try:
next_item = input[index]
except IndexError:
output.append(head)
input.remove(head)
index = 1
continue
if head & next_item:
head.update(next_item)
input.remove(next_item)
index = 1
else:
index += 1
return output
Here is a function that does what you want. Probably not the most pythonic one but does the job, most likely can be improved a lot.
from sets import Set
A = [set([1,2]), set([3,4]), set([2,3]) ]
merges = any( a&b for a in A for b in A if a!=b)
while(merges):
B = [A[0]]
for a in A[1:] :
merged = False
for i,b in enumerate(B):
if a&b :
B[i]=b | a
merged =True
break
if not merged:
B.append(a)
A = B
merges = any( a&b for a in A for b in A if a!=b)
print B
What is happening there is the following, we loop all the sets in A, (except the first since we added that to B already. We check the intersection with all the sets in B, if the intersection result anything but False (aka empty set) we perform a union on the set and start the next iteration, about set operation check this page:
https://docs.python.org/2/library/sets.html
& is intersection operator
| is union operator
You can probably go more pythonic using any() etc but wuold have required more processing so I avoided that
Hello I am trying to develop a function to find duplicates in a list. Below is the code that I have obtained thus far. I am cannot seem to figure out how to get the code to correctly add the number of duplicated numbers.
import collections
myList = [5, 9, 14, 5, 2, 5, 1]
def find_duplicates(aList, target):
if target not in aList:
print (target, "occurred 0 times")
else:
n=0
print (target, "occurred",n+1,"times")
the output of the code shows:
find_duplicates(myList, 5)
5 occurred 1 times
Obviously I am missing something for the program to properly track how many times the value occurs? Can someone please help?
I am not allowed to use the count() or sort() built in functions.
To only count the number of duplicates, just iterate over the list, comparing each value. If you find a match, increment a counter, than report the counter. To make this better, I would return the count then print to console outside of the def.
import collections
def find_duplicates(aList, target):
n = 0
for obj in aList:
if obj is target:
n += 1
return n
myList = [5, 9, 14, 5, 2, 5, 1]
target = 5
num_dup = find_duplicates(myList, target)
print (target, "occurred", num_dup, "times")
This should echo out:
5 occurred 3 times
Or do this (with list.count(x)):
myList = [5, 9, 14, 5, 2, 5, 1]
target = 5
num_dup = myList.count(target)
print (target, "occurred", num_dup, "times")
This should echo out:
5 occurred 3 times
You forgot to increment n in your code, so it always print 1. I think your code should look like:
import collections
myList = [5, 9, 14, 5, 2, 5, 1]
def find_duplicates(aList, target):
if target not in aList:
print (target, "occurred 0 times")
else:
n= aList.count(5)
print (target, "occurred",n,"times")
without using count and reading the target from shell:
import collections
myList = [5, 9, 14, 5, 2, 5, 2]
def find_duplicates(aList, target):
result = 0
for item in aList:
if item == target:
result += 1
return result
try:
target = int(raw_input("Choose a number to find duplicates: ")) # for python 3.X use input instead of raw_input
res = find_duplicates(myList, target)
print (target, " occurred ", res, " times")
except:
print("Write a number, not anything else")
This works for integers, if you want to use floats, just change int(...) for float(...)
it's a simple case of using a dictionary. Check the following code:
def frequency(l):
counter = {}
for x in l:
counter[x] = counter.get(x, 0) + 1
return counter
It will iterate over the list, saving each element as a key to the counter dictionary. Note the special form counter.get(x, 0), it will return the value of counter[x] if x is already on the dict, else it will return zero.
Checking the results is a matter of using:
print(frequency(myList))
>>> {9: 1, 2: 1, 5: 3, 14: 1, 1: 1}
You can get the number of appearances of any member by inspecting the dictionary:
frq = frequency(myList)
print(frq[14])
>>> 1
print(frq[1])
>>> 1
Of course it's possible to write a wrapper:
def target_frequencty(target, my_list):
frq = frequencty(my_list)
return frq.get(target, 0)
Enjoy.
I'd like to do a random shuffle of a list but with one condition: an element can never be in the same original position after the shuffle.
Is there a one line way to do such in python for a list?
Example:
list_ex = [1,2,3]
each of the following shuffled lists should have the same probability of being sampled after the shuffle:
list_ex_shuffled = [2,3,1]
list_ex_shuffled = [3,1,2]
but the permutations [1,2,3], [1,3,2], [2,1,3] and [3,2,1] are not allowed since all of them repeat one of the elements positions.
NOTE: Each element in the list_ex is a unique id. No repetition of the same element is allowed.
Randomize in a loop and keep rejecting the results until your condition is satisfied:
import random
def shuffle_list(some_list):
randomized_list = some_list[:]
while True:
random.shuffle(randomized_list)
for a, b in zip(some_list, randomized_list):
if a == b:
break
else:
return randomized_list
I'd describe such shuffles as 'permutations with no fixed points'. They're also known as derangements.
The probability that a random permutation is a derangement is approximately 1/e (fun to prove). This is true however long the list. Thus an obvious algorithm to give a random derangement is to shuffle the cards normally, and keep shuffling until you have a derangement. The expected number of necessary shuffles is about 3, and it's rare you'll have to shuffle more than ten times.
(1-1/e)**11 < 1%
Suppose there are n people at a party, each of whom brought an umbrella. At the end of the party, each person takes an umbrella at random from the basket. What is the probability that no-one holds their own umbrella?
You could generate all possible valid shufflings:
>>> list_ex = [1,2,3]
>>> import itertools
>>> list(itertools.ifilter(lambda p: not any(i1==i2 for i1,i2 in zip(list_ex, p)),
... itertools.permutations(list_ex, len(list_ex))))
[(2, 3, 1), (3, 1, 2)]
For some other sequence:
>>> list_ex = [7,8,9,0]
>>> list(itertools.ifilter(lambda p: not any(i1==i2 for i1,i2 in zip(list_ex, p)),
... itertools.permutations(list_ex, len(list_ex))))
[(8, 7, 0, 9), (8, 9, 0, 7), (8, 0, 7, 9), (9, 7, 0, 8), (9, 0, 7, 8), (9, 0, 8, 7), (0, 7, 8, 9), (0, 9, 7, 8), (0, 9, 8, 7)]
You could also make this a bit more efficient by short-circuiting the iterator if you just want one result:
>>> list_ex = [1,2,3]
>>> i = itertools.ifilter(lambda p: not any(i1==i2 for i1,i2 in zip(list_ex, p)),
... itertools.permutations(list_ex, len(list_ex)))
>>> next(i)
(2, 3, 1)
But, it would not be a random choice. You'd have to generate all of them and choose one for it to be an actual random result:
>>> list_ex = [1,2,3]
>>> i = itertools.ifilter(lambda p: not any(i1==i2 for i1,i2 in zip(list_ex, p)),
... itertools.permutations(list_ex, len(list_ex)))
>>> import random
>>> random.choice(list(i))
(2, 3, 1)
Here is another take on this. You can pick one solution or another depending on your needs. This is not a one liner but shuffles the indices of elements instead of the elements themselves. Thus, the original list may have duplicate values or values of types that cannot be compared or may be expensive to compare.
#! /usr/bin/env python
import random
def shuffled_values(data):
list_length = len(data)
candidate = range(list_length)
while True:
random.shuffle(candidate)
if not any(i==j for i,j in zip(candidate, range(list_length))):
yield [data[i] for i in candidate]
list_ex = [1, 2, 3]
list_gen = shuffled_values(list_ex)
for i in range(0, 10):
print list_gen.next()
This gives:
[2, 3, 1]
[3, 1, 2]
[3, 1, 2]
[2, 3, 1]
[3, 1, 2]
[3, 1, 2]
[2, 3, 1]
[2, 3, 1]
[3, 1, 2]
[2, 3, 1]
If list_ex is [2, 2, 2], this method will keep yielding [2, 2, 2] over and over. The other solutions will give you empty lists. I am not sure what you want in this case.
Use Knuth-Durstenfeld to shuffle the list. As long as it is found to be in the original position during the shuffling process, a new shuffling process is started from the beginning until it returns to a qualified arrangement. The time complexity of this algorithm is the smallest constant term:
def _random_derangement(x: list, randint: Callable[[int, int], int]) -> None:
'''
Random derangement list x in place, and return None.
An element can never be in the same original position after the shuffle. provides uniform distribution over permutations.
The formal parameter randint requires a callable object such as rand_int(b, a) that generates a random integer within the specified closed interval.
'''
from collections import namedtuple
sequence_type = namedtuple('sequence_type', ('sequence_number', 'elem'))
x_length = len(x)
if x_length > 1:
for i in range(x_length):
x[i] = sequence_type(sequence_number = i, elem = x[i])
end_label = x_length - 1
while True:
for i in range(end_label, 0, -1):
random_location = randint(i, 0)
if x[random_location].sequence_number != i:
x[i], x[random_location] = x[random_location], x[i]
else:
break
else:
if x[0].sequence_number != 0: break
for i in range(x_length):
x[i] = x[i].elem
complete_shuffle
Here's another algorithm. Take cards at random. If your ith card is card i, put it back and try again. Only problem, what if when you get to the last card it's the one you don't want. Swap it with one of the others.
I think this is fair (uniformally random).
import random
def permutation_without_fixed_points(n):
if n == 1:
raise ArgumentError, "n must be greater than 1"
result = []
remaining = range(n)
i = 0
while remaining:
if remaining == [n-1]:
break
x = i
while x == i:
j = random.randrange(len(remaining))
x = remaining[j]
remaining.pop(j)
result.append(x)
i += 1
if remaining == [n-1]:
j = random.randrange(n-1)
result.append(result[j])
result[j] = n
return result