Related
I have the following loop:
import random
distribution = []
sum = 0
while True:
item = random.randint(1, 8)
if sum + item == 812:
distribution.append(item)
break
elif sum + item > 812:
distribution.append(812 - sum)
break
else:
distribution.append(item)
sum = sum + item
The idea here is to create a list with items where each item is a random number between 1 and 8 and the sum of items in the list has to be exactly 812. The challenge is, that the last item can randomly overshoot, so a simple stop condition won't do.
I'm racking my brain on how to express this functionally, but came up blank so far. Any ideas?
I am answering the title of this question:
What is the functional-style replacement of this loop?
def random_list(sum_rest):
item = random.randint(1, min(8, sum_rest))
return ([] if sum_rest - item == 0 else random_list(sum_rest - item)) + [item]
This solution is based on the improvement proposed by #DollarAkshay, once you get that, it is very straightforward: I am recursively calling random_list, but decreasing the sum_rest parameter by the item randomly chosen.
You get the list by calling
random_list(812)
A disciplined (and practical) approach is to generate sequences, stopping when the sum is at least 812, and then restarting if the sum is above 812. This is called "rejection sampling". This guarantees that you are picking sequences fairly, and not skewing the randomness.
You expect approximately 1 in 8 of the samples to be good, so you expect to generate 8 samples before you find a good one.
import random
def seqsum(n):
while True:
S = 0
s = []
while S < n:
s.append(random.randrange(8)+1)
S += s[-1]
if S == n:
return s
for _ in range(10):
print(seqsum(812))
Obviously this will be slower than skewing the last element or elements of the sequence to force the sum, but that's a trade-off for avoiding the skew.
Here's some code that prints out the distribution of the last element of the sequence, using this method, that of #hiro-protagonist and that of #DollarAkshay. It shows the frequencies of the numbers 1 to 8 in a sample of size 10000, printed as an array. You can see the heavy skew towards low numbers in those methods.
def seqhiro(n):
S = 0
s = []
while S < n:
s.append(min(812 - S, random.randint(1, 8)))
S += s[-1]
return s
def seqdollar(n):
S = 0
s = []
while S < n:
s.append(random.randint(1, min(8, 812 - S)))
S += s[-1]
return s
def sample(m):
last = [0] * 9
for _ in range(10000):
s = m(812)
last[s[-1]] += 1
return last[1:]
print('rejection', sample(seqsum))
print('hiro', sample(seqhiro))
print('dollar', sample(seqdollar))
Output:
rejection [1234, 1308, 1280, 1178, 1246, 1247, 1257, 1250]
hiro [2226, 1904, 1727, 1319, 1077, 895, 560, 292]
dollar [5220, 1803, 938, 595, 475, 393, 319, 257]
Dollar Akshay's method produces 1 as the final number approximately 20 times more than 8, and hiro's method is better, but still 1 appears approximately 7.5 times more often than 8 as the final entry.
The method of this answer (rejection sampling) gives an approximately equal distribution for all final digits in this sample of 10000 runs.
Obviously it's possible to hide the skew of the fast methods, for example by shuffling the array before returning it, but the statistically correct method in this answer has nothing to hide.
One solution is to generate random numbers such that they do not cross 812. For example when your sum is 806 you can only generate numbers between [1, 6].
So you should change your random number generator to be
item = random.randint(1, min(8, 812 - sum))
Also your entire program becomes much shorter. Note do not use the variable name sum as it is an inbuilt function
import random
distribution = []
list_sum = 0
while list_sum < 812:
item = random.randint(1, min(8, 812 - list_sum ))
distribution.append(item)
list_sum += item
this is exactly what your if statements do - just rewritten with min:
MAX = 812
distribution = []
s = 0
while s < MAX:
item = min(MAX - s, random.randint(1, 8))
distribution.append(item)
s += item
note that this will slightly skew your distribution. the last number will be consistently smaller than the others... one way to hide that a little would be to shuffle the list afterwards.
in order to have a really uniform distribution you'd have to start over in case you overshoot...
An answer that's similar to yours
import random
distribution = []
lsum = 0
while True:
item = random.randint(1, 8)
lsum = lsum + item
distribution.append(item)
if lsum > 812:
lsum = lsum - item
distribution.pop()
if lsum == 812:
break
Because you know the max value of items added, could you not just deterministically end with the last item as soon as you are within range of this max value at the end? In that case, you could do something like:
import random
def creat_random_list(total_sum, min_value=1, max_value=8):
distribution = []
current_sum = 0
done = False
while not done:
if total_sum - current_sum <= max_value:
new_item = total_sum - current_sum
done = True
else:
new_item = random.randint(min_value, max_value)
distribution.append(new_item)
current_sum += new_item
return distribution
random_numers = creat_random_list(total_sum=812)
print(f"sum {sum(random_numers)} of list {random_numers}")
Note that I have renamed your variable sum to current sum, because sum is an inbuilt function, so not a good variable name to pick
Result look like:
sum 812 of list [7, 5, 7, 4, 7, 7, 7, 1, 2, 6, 6, 4, 5, 3, 1, 4, 4, 2, 8, 7, 5, 5, 5, 5, 1, 1, 1, 4, 5, 5, 1, 1, 8, 8, 6, 5, 5, 5, 8, 2, 3, 7, 8, 6, 2, 6, 7, 4, 7, 7, 8, 7, 1, 4, 7, 2, 2, 6, 4, 4, 3, 4, 2, 6, 3, 3, 3, 4, 1, 3, 6, 6, 8, 5, 6, 3, 3, 7, 6, 8, 5, 3, 5, 4, 1, 7, 6, 5, 4, 1, 7, 1, 5, 1, 7, 3, 4, 2, 3, 3, 1, 4, 6, 5, 4, 1, 1, 1, 3, 7, 1, 3, 8, 8, 7, 4, 4, 8, 8, 8, 5, 6, 5, 6, 8, 5, 7, 2, 2, 8, 5, 1, 5, 4, 3, 2, 1, 1, 8, 8, 8, 8, 2, 1, 1, 4, 8, 4, 1, 2, 2, 2, 8, 5, 6, 5, 4, 1, 3, 4, 3, 3, 2, 2, 5, 7, 8, 1, 1, 8, 2, 7, 7, 2, 5, 1, 7, 7, 2, 3, 7, 7]
I am building a sudoku game, I found a site that can provide me 1 million pre generated games.
I downloaded the file (CSV) and want to prepare it for frontend use.
Each game is 81 numbers like
346179258187523964529648371965832417472916835813754629798261543631485792254397186
974183652651274389283596714129835476746912538835647921568329147317468295492751863
563472198219386754847195623472638519951247386638519472795864231324951867186723945
I would love to create JS or JSON file with all the puzzles in order to import it in my code.
Here is the ideal result for each game (line).
[
[5, 3, 4, 6, 7, 8, 9, 1, 2],
[6, 7, 2, 1, 9, 5, 3, 4, 8],
[1, 9, 8, 3, 4, 2, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 8, 5, 3, 7, 9, 1],
[7, 1, 3, 9, 2, 4, 8, 5, 6],
[9, 6, 1, 5, 3, 7, 2, 8, 4],
[2, 8, 7, 4, 1, 9, 6, 3, 5],
[3, 4, 5, 2, 8, 6, 1, 7, 9]
]
Here is what I managed to accomplish in python
import csv
import json
holder = []
rows = []
def divide_chunks(l, n):
# looping till length l
for i in range(0, len(l), n):
yield l[i:i + n]
with open('sudoku.csv', newline='') as csvfile:
counter = 0
n = 9
s=','
reader = csv.DictReader(csvfile)
for row in reader:
if counter > 10:
break
print(row['solutions'])
print(len(row['solutions']))
test = [int(str(row['solutions']))]
#chunk = divide_chunks(test, n)
# for val in enumerate(chunk):
# val = [val]
# # for index,item in enumerate(val):
# # item[index] = item+s
# # print(val)
holder.append(test)
counter +=1
print(holder)
with open('puzzles.json', 'w') as outputfile:
json.dump(holder,outputfile)
this is my output so far in puzzles.json
[
[864371259325849761971265843436192587198657432257483916689734125713528694542916378],
[346179258187523964529648371965832417472916835813754629798261543631485792254397186],
[695127384138459672724836915851264739273981546946573821317692458489715263562348197],
[497258316186439725253716498629381547375964182841572639962145873718623954534897261],
[465912378189473562327568149738645291954821637216397854573284916642159783891736425],
[194685237382974516657213489823491675541768923769352841215839764436527198978146352],
[289765431317924856645138729763891542521473968894652173432519687956387214178246395],
[894231657762495183351876942583624719219387564647159328128763495976542831435918276],
[563472198219386754847195623472638519951247386638519472795864231324951867186723945],
[163725948584693271729184365946358127371462589852917634498231756637549812215876493],
[974183652651274389283596714129835476746912538835647921568329147317468295492751863]
]
Any suggestions please?
Thanks
You can break the string into numbers by using a list comprehension
sudoku = 346179258187523964529648371965832417472916835813754629798261543631485792254397186
sudoku_list = [number for number in str(sudoku)]
And then you break that into chunks of length 9 by using another list comprehension
sudoku_final = [sudoku_list[9*i:9*i+9] for i in range(9)]
If you want the output to be integers instead of strings, use int(number) for number in str(sudoku) in the list comprehension
Using Python, you can separate all the numbers into a list by converting them into a string and back like so:
tuple(map(int, str(x))) # x is the 81 digit number.
With that, you can separate the list of 81 digits into a 9x9 grid like so (iterating through every multiple of 9 and taking the next 9 numbers):
[x[i: i + 9] for i in range(0, 81, 9)]
list = [0, 1, 2, 3, 4, 1, 5, 0, 6, 5, 7, 8, 9, 10, 11, 12, 13, 2]
list is used "matrix like"
1. 0 1 2
2. 3 4 1
3. 5 0 6
... and so on. I would like to write all those lines into a new list/matrix, but without lines, that would repeat a number. However the order of a line has to be preserved.
So far I use this:
compa = [0,1,2,3,4,1,5,0,6,5,7,8,9,10,11,12,13,2] #the list to be used as base
temp = [0,1,2] #new list starts from the first element
temp2 = [12,13,2] #new list starts from the last element
Mischzahl = 3 #defines the number of elements in a line of the "matrix"
n = 0
while n < len(compa):
for m in range(0,len(temp)):
if temp[m] == compa[n]:
n = (int(n/Mischzahl) + 1) * Mischzahl - 1 #calculates the "foul" line and sets n to the next line
break
if (n + 1) % Mischzahl == 0 and m == len(temp) - 1 : #if the end of temp is reached, the current line is transferred to temp.
for p in range(Mischzahl):
temp.append(compa[Mischzahl*int(n/Mischzahl) + p])
n += 1
and the same backwards
n = len(compa) - 1
while n > 0: #same as above but starting from last element
for m in range(len(temp2)):
if temp2[m] == compa[n]:
n = (int(n/Mischzahl) - 1) * Mischzahl + Mischzahl
break
if (n) % Mischzahl == 0 and m == len(temp2) - 1:
for p in range(Mischzahl):
temp2.append(compa[Mischzahl*int(n/Mischzahl) + p])
n = n - 1
resulting output for temp and temp2:
[0, 1, 2, 3, 4, 1, 5, 0, 6, 5, 7, 8, 9, 10, 11, 12, 13, 2] #compa
[0, 1, 2, 5, 7, 8, 9, 10, 11] #temp
[12, 13, 2, 9, 10, 11, 5, 7, 8, 3, 4, 1] #temp2
Since this is the most time-consuming part of the script: Is there a more efficient way to do this? Any helpful advice or direction would be highly welcome.
You can define a function that iterates over the list in strides of a given length (in your case 3), checks if the elements of the stride are in a set of numbers, if not extend the out list and update the set.
from math import ceil
def unique_by_row(compa, stride_size=3, reverse=False):
strides = ceil(len(compa)/stride_size)
out = []
check = set()
it = range(strides)
if reverse:
it = reversed(it)
for i in it:
x = compa[stride_size*i:stride_size*(i+1)]
if not check.intersection(x):
out.extend(x)
check.update(x)
return out
Tests:
compa = [0, 1, 2, 3, 4, 1, 5, 0, 6, 5, 7, 8, 9, 10, 11, 12, 13, 2]
unique_by_row(compa)
# returns:
[0, 1, 2, 5, 7, 8, 9, 10, 11]
unique_by_row(compa, reverse=True)
# returns:
[12, 13, 2, 9, 10, 11, 5, 7, 8, 3, 4, 1]
i want to write a function that takes in a list of numbers (positive integers) and returns a list of sorted numbers such that odd numbers come first and even numbers come last
For example:
my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) => [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
my_sort([1, 2]) => [1, 2]
my_sort([2, 1]) => [1, 2]
my_sort([3, 3, 4]) => [3, 3, 4]
my_sort([90, 45, 66]) => [45, 66, 90]'''
This is my code
def my_sort(numbers):
a = [n for n in numbers if n % 2 != 0]
b = [n for n in numbers if n % 2 == 0]
new_num = b + a
for m in numbers:
if a and b:
return new_num
else:
return "Invalid sorted output"
Which fails all the test. I'm new to programming and python. So I'ld appreciate if anyone could help me with this.
And here is the unittest
import unittest
class MySortTestCases(unittest.TestCase):
def setUp(self):
self.result1 = my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
self.result2 = my_sort([1, 2])
self.result3 = my_sort([2, 1])
self.result4 = my_sort([3, 3, 4])
self.result5 = my_sort([90, 45, 66])
def test_output_1(self):
self.assertEqual(self.result1, [1, 3, 5, 7, 9, 2, 4, 6, 8, 10],
msg='Invalid sorted output')
def test_output_2(self):
self.assertEqual(self.result2, [1, 2], msg='Invalid sorted
output')
def test_output_3(self):
self.assertEqual(self.result3, [1, 2], msg='Invalid sorted
output')
def test_output_4(self):
self.assertEqual(self.result4, [3, 3, 4], msg='Invalid sorted
output')
def test_output_5(self):
self.assertEqual(self.result5, [45, 66, 90], msg='Invalid
sorted output')
You could so it by splitting the list into odd and even and then sorting both and concatenating the two lists.
def my_sort(numbers):
odd = [n for n in numbers if n % 2 != 0]
even = [n for n in numbers if n % 2 == 0]
return sorted(odd) + sorted(even)
See that this
>>> my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
But using a key function avoids constructing the split lists:
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers.sort(key=lambda v: (v%2==0, v))
>>> numbers
[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
This sorts the list using a key function that returns a tuple of (0, v) if v is odd and (1, v) if even. This causes the odd numbers to appear before the even numbers in an ascending ordered sort.
It can be turned into a function:
def my_sort(numbers):
return sorted(numbers, key=lambda v: (v%2==0, v))
So, I went ahead and grabbed your code and did just a few tests by myself to see the actual output. Your code doesn't actually sort the list currently. It only puts the even numbers first and the odd numbers second. Which by the sound of it isn't what you want either.
>>> my_sort([4, 5, 7, 1, 2, 6, 3])
[4, 2, 6, 5, 7, 1, 3]
>>> my_sort([1, 2])
[2, 1]
These were the outputs I got by running your code using the Python interpreter. As you can see the even numbers are first, unsorted, followed by the odd numbers, also unsorted. This just has to do with the way you made the new_num list. You have new_num = b + a, but you created b by looking for all the even numbers b = [n for n in numbers if n % 2 == 0] and created a by looking for all the odd numbers a = [n for n in numbers if n % 2 != 0]. The % returns the remainder. So, if a number is divisible by 2 it returns 0 and this means that it is even. So, you can either flip the assignment of a and b or you can flip when you add them together so a is first and b is second.
As for the individual chunks not being sorted. Python has a built in sorted function that you can call on list sorted(my_list) that returns a sorted version of that list. So, if you just run that on your a and b when you're adding them together to create your new_num list then the numbers for each should be sorted just fine.
Your if statements at the end inside your for loop are also not working properly. Giving an already sorted list just returns the list given. Your code here:
for m in numbers:
if a and b:
return new_num
else:
return "Invalid sorted output"
This is looping through the original list given and returns the new_num if a and b exists. Since a and b will always exist this will always return new_num and never the "Invalid sorted output" statement. You need to make sure you're checking to see if the numbers list is already sorted with the expected output of your function. My suggestion would be check to see if new_num is the same as numbers.
Just use sorted twice. First to sort the list, and again to sort it by odd then even using the key parameter.
x = [3,5,4,1,6,8,10,2,9,7]
sorted(sorted(x), key=lambda x: (x+1)%2)
# returns:
# [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
This should work... It makes sense if you are joining Andela.
def my_sort(theNumbers):
#pass
evenNumbers=[]
oddNumbers=[]
#CHECK IF EVEN OR ODD THEN STORE THEN SEPARATE
for i in theNumbers:
if i%2==0:
evenNumbers.append(i)
else:
oddNumbers.append(i)
#print (evenNumbers)
#SORT DEM LISTS
evenNumbers.sort()
oddNumbers.sort()
#print (evenNumbers)
#join the two
oddNumbers+=evenNumbers
print (oddNumbers)
#my_sort([11111, 1, 11, 979749, 1111, 1111])
This code will sort even and odd numbers without creating a temporary list.
def segregateEvenOdd(arr, index=0, iterations=0):
if iterations == len(arr):
return arr
if arr[index]%2 == 0:
arr.append(arr[index])
arr.pop(index)
return segregateEvenOdd(arr, index, iterations+1 )
if arr[index]%2 != 0:
return segregateEvenOdd(arr,index+1,iterations+1)
arr = [ 2, 3, 9, 45, 2, 5, 10, 47 ]
output = segregateEvenOdd(arr)
print(output)
# Output
# [3, 9, 45, 5, 47, 2, 2, 10]
This code should work:
def sort_integers(list_of_integers):
odd_numbers = [n for n in list_of_integers if n%2!=0]
odd_numbers = sorted(odd_numbers, reverse = True)
print(odd_numbers)
even_numbers = [x for x in list_of_integers if x%2 == 0]
even_numbers = sorted(even_numbers, reverse = True)
print(even_numbers)
new_sorted_list = even_numbers + odd_numbers
print(new_sorted_list)
I am trying to come up with a function to split the length of a list evenly depending on it's original length.
So for example if I have a dataset returned that is 2000 I would like to split it into 4. Whereas if the dataset is 1500 split it into 3.
Then to call the function:
Thread_A_DATA, Thread_B_DATA = split_list( SQL_RETURN )
I would like to do something like the following:
if len(dataset) <= 1000:
# Split in 2
a, b = split_list(dataset, 2)
if len(dataset) > 1000 or len(dataset) <= 1500:
# Split in 3
a, b, c = split_list(dataset, 3)
# etc etc...
I've managed to split a dataset in half using this code found previously on stackoverflow:
def split_list( a_list ):
half = len( a_list ) / 2
return a_list[:half], a_list[half:]
But I can't work it out with 3,4 or 5 splits!
If anyone can help that would be great.
Thanks in advance.
As I understand the question, you don't want to split every 500 elements but instead split in 2 if there are less than 1000 elements, in 3 if less than 1500, 4 for 2000, etc. But if there are 1700 elements, you would split in 4 groups of 425 elements (that's what I understand by "split evenly").
So, here's my solution:
def split_list(a_list, number_of_splits):
step = len(a_list) / number_of_splits + (1 if len(a_list) % number_of_splits else 0)
return [a_list[i*step:(i+1)*step] for i in range(number_of_splits)]
l = [1, 8, 2, 3, 4, 5, 6, 7, 1, 5, 3, 1, 2, 5]
print l
print split_list(l, 3)
print split_list(l, 2)
Output
[1, 8, 2, 3, 4, 5, 6, 7, 1, 5, 3, 1, 2, 5]
[[1, 8, 2, 3, 4], [5, 6, 7, 1, 5], [3, 1, 2, 5]]
[[1, 8, 2, 3, 4, 5, 6], [7, 1, 5, 3, 1, 2, 5]]
edit: Python 3 version:
def split_list(a_list, number_of_splits):
step = len(a_list) // number_of_splits + (1 if len(a_list) % number_of_splits else 0)
return [a_list[i*step:(i+1)*step] for i in range(number_of_splits)]
l = [1, 8, 2, 3, 4, 5, 6, 7, 1, 5, 3, 1, 2, 5]
print(l)
print(split_list(l, 3))
print(split_list(l, 2))
Python 3
def splitList(L):
return[L[i:i+500] for i in range(0, len(L), 500)]
Python 2
def splitList(L):
return[L[i:i+500] for i in xrange(0, len(L), 500)]
def split_it(a_list,size_of_split):
return zip(*[iter(a_list)]*size_of_split)
is fun
print split_it(range(100),3) # splits it into groups of 3
unfortunatly this will truncate the end of the list if it does not divide evenly into split_size ... you can fix it like so
return zip(*[iter(a_list)]*size_of_split) + [tuple(a_list[-(len(a_list)%size_of_split):])]
if you wanted to cut it into 7 pieces say you can find the size of the split by
split_size = len(a_list) / num_splits
Python 2.7
>>> import math
>>> lst = range(35)
>>> t = 3 # how many items to be splited
>>> n = int(math.ceil(len(lst) / float(t)))
>>> res = [lst[i:i+n] for i in range(0, len(lst), n)]
>>> res
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], [24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]]