Related
I'm new to python (or programming for that matter). I'm looking to create a def function with two parameters: a list of bits, and an error probability.
For each element in the list of bits, there is a chance (error probability) that the element should be flipped from 0 to 1 or vice versa. The function should return the new list that contains the bits and the actual number of bits that were flipped.
I've been experimenting for about an hour and a half and couldn't really come up with anything. I know we're supposed to use the function random.random and a for loop inside the def function, but nothing has really worked.
The result should look something like this:
>>>x
[0,0,1,1,0,0,0,0]
>>>(NewList,FlipTimes)=TheFunction(x,0.2)
>>>NewList
[0,0,1,0,1,0,0,1]
>>>FlipTimes
3
Again, I'm very new to programming, so my attempt here is pretty futile.
def addNoise(a,b):
c=0
for x in a:
y=random.random
if y<b:
if x==1:
x=0
else:
x=1
for i in x:
if y<b== True:
c+=1
return(x,c)
import random
def flipbit(x, prob):
count = 0
out = []
for e in x:
if random.random() <= prob:
count += 1
out.append(int(not e))
else:
out.append(e)
return out, count
x = [0,0,1,0,1,0,0,1]
new_list, flip_times = flipbit(x, 0.2)
print ('original: ', x)
print ('new list: ', new_list)
print ('flip times: ', flip_times)
# original: [0, 0, 1, 0, 1, 0, 0, 1]
# new list: [0, 0, 1, 0, 1, 1, 1, 1]
# flip times: 2
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)
I have a list. For example:
[0, 0, 1, 0, 0, 1, 0]
I'd like to know what is the most effective way to count the 1 -> 0 transitions. In this case for example the answer is 2 (in the 2-3 and in the 5-6 positions)
I tried the following:
stat=[0, 0, 1, 0, 0, 1, 0]
pair1=stat[:-1]
pair2=stat[1:]
result=len([i for i in zip(pair1, pair2) if i==(1,0)])
I'm wondering if there is a better way
Here are 3 ways:
from itertools import islice
import numpy as np
lst = [0, 0, 1, 0, 0, 1, 0]
res1 = sum(i - j == 1 for i, j in zip(lst, lst[1:])) # 2
res2 = sum(i - j == 1 for i, j in zip(lst, islice(lst, 1, None))) # 2
res3 = np.sum(np.diff(lst) == -1) # 2
Explanation
First method utilises sum with a generation expression and zip to loop pairwise elements.
Second method is similar to the first but performs better as it avoids building a second list explicitly.
Third method utilises the 3rd party numpy library and is a vectorised approach.
Transforming your input data with slices and zips and cuts and folds is one way to approach it. And it's awesome to see how these generic actions can be combined to build a machine that represents our intended action, even if it arrives at the desired result in a roundabout way.
However, I think a more direct approach yields a more natural program. You can express your intention using natural descriptors and operations. Another benefit is you can more clearly visualize the space-time requirements of the process your function creates. Ie, it's easy to see switches below runs in O(n); comparatively, it's very hard to estimate space-time requirements of the "machine" implementation.
A simple recursive function
def switches (iter, last = 0):
if not iter:
return 0
first, *rest = iter
if first == last:
return switches (rest, last)
else:
return 1 + switches (rest, first)
print (switches ([ 0, 0, 1, 1, 0, 0, 1, 1, 1, 0 ]))
# 4 :(
Above, the answer is 4 because it's counting switches from 0 to 1 and switches from 1 to 0. You only want to count switches in one direction. We could modify our function like this
def switches (iter, last = 0):
if not iter:
return 0
first, *rest = iter
if first == last:
return switches (rest, last)
else:
if first == 1: # only count when switching from 1
return 1 + switches (rest, first)
else:
return 0 + switches (rest, first)
print (switches ([ 0, 0, 1, 1, 0, 0, 1, 1, 1, 0 ]))
# 2 :)
But you can see there's a clever way to condense the conditional
def switches (iter, last = 0):
if not iter:
return 0
first, *rest = iter
if first == last:
return switches (rest, last)
else:
return first + switches (rest, first)
print (switches ([ 0, 0, 1, 1, 0, 0, 1, 1, 1, 0 ]))
# 2 :)
You can use sum:
s = [0, 0, 1, 0, 0, 1, 0]
new_s = sum(abs(s[i]-s[i+1]) == 1 for i in range(0, len(s)-1, 2))
Output:
2
I was working on the coursera python project 2048 using codesculpter.
The code works fine when I try 4 x 4 or 5 x 5, but it shows error when 4 x 5 or any other when height != width. I think I must have messed up somewhere in the __init__ or other places but I couldn't figure out.
Could someone give me some suggestions?
Here is what I have tried so far:
import poc_2048_gui
import random
# Directions, DO NOT MODIFY
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
# Offsets for computing tile indices in each direction.
# DO NOT MODIFY this dictionary.
OFFSETS = {UP: (1, 0),
DOWN: (-1, 0),
LEFT: (0, 1),
RIGHT: (0, -1)}
def merge(line):
"""
Helper function that merges a single row or column in 2048
"""
# creat output list and remove 0
after_merge=[]
storage = []
for num_1 in range(len(line)):
after_merge.append(0)
if line[num_1] != 0 :
storage.append(line[num_1])
# sum number
for num_2 in range(len(storage)):
if num_2+2> len(storage):
break
elif storage[num_2]==storage[num_2+1]:
storage[num_2]*=2
storage.pop(num_2+1)
# replace 0 in after merge
for num in range(len(storage)):
after_merge[num]=storage[num]
return after_merge
class TwentyFortyEight:
"""
Class to run the game logic.
"""
def __init__(self, grid_height, grid_width):
self.grid_height = grid_height
self.grid_width = grid_width
self.cell=[]
self.indices = {}
self.indices[UP] = [[0,n] for n in range(grid_width)]
self.indices[LEFT] = [[n,0] for n in range(grid_height)]
self.indices[RIGHT] = [[n, grid_width - 1] for n in range(grid_height)]
self.indices[DOWN] = [[grid_height - 1, n]for n in range(grid_width)]
self.ranges = {}
self.ranges[UP] = grid_height
self.ranges[DOWN] = grid_height
self.ranges[LEFT] = grid_width
self.ranges[RIGHT] = grid_width
#self.reset()
def reset(self):
"""
Reset the game so the grid is empty except for two
initial tiles.
"""
self.cell = [[0*(col+row) for row in range(self.grid_height)] for col in range (self.grid_width)]
for count in range(2):
self.new_tile()
def __str__(self):
"""
Return a string representation of the grid for debugging.
"""
a_str = ""
for row in range(self.grid_height):
for col in range (self.grid_width):
a_str += ( str(self.cell[row][col]) + " " )
a_str += '\n'
return a_str
def get_grid_height(self):
"""
Get the height of the board.
"""
# replace with your code
return self.grid_height
def get_grid_width(self):
"""
Get the width of the board.
"""
# replace with your code
return self.grid_width
def move(self, direction):
"""
Move all tiles in the given direction and add
a new tile if any tiles moved.
"""
a_list = []
has_moved = False
for index in self.indices[direction]:
for step in range(self.ranges[direction]):
a_list.append(self.cell[index[0] + OFFSETS[direction][0] * step]
[index[1] + OFFSETS[direction][1] * step])
merged_list = merge(a_list)
if merged_list != a_list:
for step in range(self.ranges[direction]):
self.cell[index[0] + OFFSETS[direction][0] * step] \
[index[1] + OFFSETS[direction][1] * step] = merged_list[step]
has_moved = True
a_list = []
if has_moved:
self.new_tile()
def new_tile(self):
"""
Create a new tile in a randomly selected empty
square. The tile should be 2 90% of the time and
4 10% of the time.
"""
# replace with your code
row=0
col=0
available_positions = []
for row in range(self.grid_height):
for col in range(self.grid_width):
if self.cell[row][col] == 0:
available_positions.append([row, col])
if not available_positions:
print "There are no available positions."
random_pos=random.choice(available_positions)
rand_val=random.randint(1,10)
if rand_val>=9:
new_tile=4
else:
new_tile=2
self.set_tile(random_pos[0], random_pos[1], new_tile)
def set_tile(self, row, col, value):
"""
Set the tile at position row, col to have the given value.
"""
# replace with your code
self.cell[row][col] = value
def get_tile(self, row, col):
"""
Return the value of the tile at position row, col.
"""
# replace with your code
return self.cell[row][col]
poc_2048_gui.run_gui(TwentyFortyEight(4, 4))
Okay, I didn't debug all the way but here's what I found. cell should have dimension grid_height * grid_width. Correct?
However, right before this loop:
for row in range(self.grid_height):
for col in range(self.grid_width):
print(row," ",col);
if self.cell[row][col] == 0:
available_positions.append([row, col])
if not available_positions:
print "There are no available positions."
I found that the size of cell is reverse. That is grid_width * grid_height. Put these two lines before the nested loops to see for yourself.
print("cell size",len(self.cell)," ",len(self.cell[0]))
print("grid size",self.grid_height," ",self.grid_width)
This will cause IndexError in line if self.cell[row][col] == 0: when the dimensions are different. That being said, you should step through and see exactly how you fill in both the grid and the cell. Make sure they correspond correctly.
Hope that helps!
I did a quick debug and I was able to get a IndexError when calling move(). Looking through, you seem to expect self.cell to be populated, but it only is ever populated through your reset() function. You may not see this if your UI module calls reset when initializing...
There is a second IndexError then when the row and col are not the same number. This (as mentioned in the other answer) is because your 2D array representation is col * row, not row * col
below is the printout of (4, 6), which has 4 COLUMN and 6 ROW. You likely just need to swap the two in your representation:
[0, 0, 0, 0]
[2, 0, 2, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
A potential improvement to your syntax, but you can initiate your cells as such (test with your usage...)
self.cell = [[[0] * self.grid_width] for row in xrange(self.grid_height)]
Lastly, I believe you may get an IndexError in your new_tile because Python lists begin at the 0th element. You'll want to iter 0 through n-1:
for row in range(self.grid_height-1):
for col in range(self.grid_width-1):
How can I randomly shuffle a list so that none of the elements remains in its original position?
In other words, given a list A with distinct elements, I'd like to generate a permutation B of it so that
this permutation is random
and for each n, a[n] != b[n]
e.g.
a = [1,2,3,4]
b = [4,1,2,3] # good
b = [4,2,1,3] # good
a = [1,2,3,4]
x = [2,4,3,1] # bad
I don't know the proper term for such a permutation (is it "total"?) thus having a hard time googling. The correct term appears to be "derangement".
After some research I was able to implement the "early refusal" algorithm as described e.g. in this paper [1]. It goes like this:
import random
def random_derangement(n):
while True:
v = [i for i in range(n)]
for j in range(n - 1, -1, -1):
p = random.randint(0, j)
if v[p] == j:
break
else:
v[j], v[p] = v[p], v[j]
else:
if v[0] != 0:
return tuple(v)
The idea is: we keep shuffling the array, once we find that the permutation we're working on is not valid (v[i]==i), we break and start from scratch.
A quick test shows that this algorithm generates all derangements uniformly:
N = 4
# enumerate all derangements for testing
import itertools
counter = {}
for p in itertools.permutations(range(N)):
if all(p[i] != i for i in p):
counter[p] = 0
# make M probes for each derangement
M = 5000
for _ in range(M*len(counter)):
# generate a random derangement
p = random_derangement(N)
# is it really?
assert p in counter
# ok, record it
counter[p] += 1
# the distribution looks uniform
for p, c in sorted(counter.items()):
print p, c
Results:
(1, 0, 3, 2) 4934
(1, 2, 3, 0) 4952
(1, 3, 0, 2) 4980
(2, 0, 3, 1) 5054
(2, 3, 0, 1) 5032
(2, 3, 1, 0) 5053
(3, 0, 1, 2) 4951
(3, 2, 0, 1) 5048
(3, 2, 1, 0) 4996
I choose this algorithm for simplicity, this presentation [2] briefly outlines other ideas.
References:
[1] An analysis of a simple algorithm for random derangements. Merlini, Sprugnoli, Verri. WSPC Proceedings, 2007.
[2] Generating random derangements. MartÃnez, Panholzer, Prodinger.
Such permutations are called derangements. In practice you can just try random permutations until hitting a derangement, their ratio approaches the inverse of 'e' as 'n' grows.
As a possible starting point, the Fisher-Yates shuffle goes like this.
def swap(xs, a, b):
xs[a], xs[b] = xs[b], xs[a]
def permute(xs):
for a in xrange(len(xs)):
b = random.choice(xrange(a, len(xs)))
swap(xs, a, b)
Perhaps this will do the trick?
def derange(xs):
for a in xrange(len(xs) - 1):
b = random.choice(xrange(a + 1, len(xs) - 1))
swap(xs, a, b)
swap(len(xs) - 1, random.choice(xrange(n - 1))
Here's the version described by Vatine:
def derange(xs):
for a in xrange(1, len(xs)):
b = random.choice(xrange(0, a))
swap(xs, a, b)
return xs
A quick statistical test:
from collections import Counter
def test(n):
derangements = (tuple(derange(range(n))) for _ in xrange(10000))
for k,v in Counter(derangements).iteritems():
print('{} {}').format(k, v)
test(4):
(1, 3, 0, 2) 1665
(2, 0, 3, 1) 1702
(3, 2, 0, 1) 1636
(1, 2, 3, 0) 1632
(3, 0, 1, 2) 1694
(2, 3, 1, 0) 1671
This does appear uniform over its range, and it has the nice property that each element has an equal chance to appear in each allowed slot.
But unfortunately it doesn't include all of the derangements. There are 9 derangements of size 4. (The formula and an example for n=4 are given on the Wikipedia article).
This should work
import random
totalrandom = False
array = [1, 2, 3, 4]
it = 0
while totalrandom == False:
it += 1
shuffledArray = sorted(array, key=lambda k: random.random())
total = 0
for i in array:
if array[i-1] != shuffledArray[i-1]: total += 1
if total == 4:
totalrandom = True
if it > 10*len(array):
print("'Total random' shuffle impossible")
exit()
print(shuffledArray)
Note the variable it which exits the code if too many iterations are called. This accounts for arrays such as [1, 1, 1] or [3]
EDIT
Turns out that if you're using this with large arrays (bigger than 15 or so), it will be CPU intensive. Using a randomly generated 100 element array and upping it to len(array)**3, it takes my Samsung Galaxy S4 a long time to solve.
EDIT 2
After about 1200 seconds (20 minutes), the program ended saying 'Total Random shuffle impossible'. For large arrays, you need a very large number of permutations... Say len(array)**10 or something.
Code:
import random, time
totalrandom = False
array = []
it = 0
for i in range(1, 100):
array.append(random.randint(1, 6))
start = time.time()
while totalrandom == False:
it += 1
shuffledArray = sorted(array, key=lambda k: random.random())
total = 0
for i in array:
if array[i-1] != shuffledArray[i-1]: total += 1
if total == 4:
totalrandom = True
if it > len(array)**3:
end = time.time()
print(end-start)
print("'Total random' shuffle impossible")
exit()
end = time.time()
print(end-start)
print(shuffledArray)
Here is a smaller one, with pythonic syntax -
import random
def derange(s):
d=s[:]
while any([a==b for a,b in zip(d,s)]):random.shuffle(d)
return d
All it does is shuffles the list until there is no element-wise match. Also, be careful that it'll run forever if a list that cannot be deranged is passed.It happens when there are duplicates. To remove duplicates simply call the function like this derange(list(set(my_list_to_be_deranged))).
import random
a=[1,2,3,4]
c=[]
i=0
while i < len(a):
while 1:
k=random.choice(a)
#print k,a[i]
if k==a[i]:
pass
else:
if k not in c:
if i==len(a)-2:
if a[len(a)-1] not in c:
if k==a[len(a)-1]:
c.append(k)
break
else:
c.append(k)
break
else:
c.append(k)
break
i=i+1
print c
A quick way is to try to shuffle your list until you reach that state. You simply try to shuffle your list until you are left with a list that satisfies your condition.
import random
import copy
def is_derangement(l_original, l_proposal):
return all([l_original[i] != item for i, item in enumerate(l_proposal)])
l_original = [1, 2, 3, 4, 5]
l_proposal = copy.copy(l_original)
while not is_derangement(l_original, l_proposal):
random.shuffle(l_proposal)
print(l_proposal)