Optimize brute force with permutations - python-3.x

EXPLAINING WHAT THE SCRIPT DOES
I made a python script where the goal is to balance marbles on a circular board. Marble 1 weighs 1 Unit, 2 weighs 2 Units, and so on. The goal is to find the best order so it's a balanced as possible.
PROBLEM
I also made a method that tries all the possibilities with permutations. I get a memory error if I try with more than 10 marbles (3628800 possibilites).
Is there any way to optimize the code with either multi threading/ multiprocessing, maybe a better way than permutations?
CODE
# balance_game.py
# A program used to your skills in balancing marbles on a board
from itertools import permutations
from math import cos, radians, pow, sin, sqrt
from time import time
# Checks if marbles will balance on a circular board
# Marble 1 weighs 1 unit, 2 weighs 2 units, and so on
def test_your_might(NUMBER_OF_MARBLES, marbles):
angle = 360 / NUMBER_OF_MARBLES
angles = [angle * n for n in range(1, NUMBER_OF_MARBLES + 1)]
X = []
Y = []
Fx = []
Fy = []
i = 0
for n in range(0, NUMBER_OF_MARBLES):
angle = radians(angles[i])
X.append(cos(angle))
Y.append(sin(angle))
i += 1
for n in range(0, NUMBER_OF_MARBLES):
Fx.append(X[n] * marbles[n])
for n in range(0, NUMBER_OF_MARBLES):
Fy.append(Y[n] * marbles[n])
return sqrt(pow(sum(Fx), 2) + pow(sum(Fy), 2))
def brute_force_solution(NUMBER_OF_MARBLES):
possibilities = permutations([x for x in range(1, NUMBER_OF_MARBLES + 1)])
solutions = {}
for possibility in possibilities:
possibility = list(possibility)
solution = test_your_might(NUMBER_OF_MARBLES, possibility)
solutions[str(possibility)] = solution
return solutions
# print(test_your_might(5, [5, 1, 4, 3, 2]))
t0 = time()
solutions = brute_force_solution(10)
t1 = time()
best_order = min(solutions, key=solutions.get)
lowest_score = solutions[best_order]
print(f"Lowest score: {lowest_score}\nOrder: {best_order}")
print(f"It took {t1-t0} seconds to find the best possibility")
print(f"There were {len(solutions)} possibilities")
FYI
The method is brute_force_solution

Since the bottleneck is CPU usage, multithreading won't do a lot to help here, but multiprocessing should. Not an expert but have been experimenting with parallelism recently so will have a play around and update this answer if I get anywhere. (EDIT: I have tried a number of attempts at using multiprocessing but I've only succeeded in increasing the run time!)
It might be that you need to store all solutions, but if not, one small optimisation in terms of time, but huge in terms of memory, would be to not store all the possible results and just store the best result, so you're not creating another very long array needlessly. Ideally you could calculate number of solutions directly since it only depends on NUMBER_OF_MARBLES but have included it in the function to be consistent.
def brute_force_solution(NUMBER_OF_MARBLES):
possibilities = permutations([x for x in range(1, NUMBER_OF_MARBLES + 1)])
# count the number of solutions
number_of_solutions = 0
best_solution_so_far = None
for possibility in possibilities:
number_of_solutions += 1
possibility = list(possibility)
solution = test_your_might(NUMBER_OF_MARBLES, possibility)
# If this solution is the best so far, record the score and configuration of marbles.
if (best_solution_so_far is None) or (solution < best_solution_so_far[1]):
best_solution_so_far = (str(possibility), solution)
# Return the best solution and total number of solutions tested.
return (best_solution_so_far, number_of_solutions)
t0 = time()
one_solution = brute_force_solution(11)
t1 = time()
best_order = one_solution[0][0]
best_score = one_solution[0][1]
number_of_solutions = one_solution[1]
It took a while but it ran with 11 marbles:
>>>Lowest score: 0.00021084993450850984
>>>Order: [10, 7, 3, 4, 11, 1, 8, 9, 5, 2, 6]
>>>It took 445.57227993011475 seconds to find the best possibility
>>>There were 39916800 possibilities
and was marginally quicker when run for 10 (and note that you aren't including the sorting of your results in your timing which is not needed with this new method and adds almost another second to your time to get the best solution):
Old
Lowest score: 1.608181078507726e-17
Order: [1, 7, 3, 10, 4, 6, 2, 8, 5, 9]
It took 43.81806421279907 seconds to find the best possibility
There were 3628800 possibilities
New
Lowest score: 1.608181078507726e-17
Order: [1, 7, 3, 10, 4, 6, 2, 8, 5, 9]
It took 37.06034016609192 seconds to find the best possibility
There were 3628800 possibilities

Related

Sieve of Eratosthenes with nested for loops

This is a very simple question about python. I was trying to get a list of prime numbers so I tried
primes = [2]
for i in primes:
for j in range(50):
if j%i != 0:
primes.append(j)
print(primes)
But it just goes into an indefinite loop.
You are looping over a list and appending to it that will make that infinite process as the iteration just takes the index but you can put an end by defining the limit value
There are lots of methods to achieve that, but i would suggest to just break if the number is bigger than the limit:
primes = [2]
limit = 40
for i in primes:
if primes[-1]>limit:break
for j in range(50):
if j%i != 0:
primes.append(j)
print(primes)
Before that, you should know what you are doing, an advice is to first do your calculations on paper and then code them.
Your code provides different results as they are not prime, first come up with an algorithm or search online.
using the provided algorithm you should be able to use the pseudocode to come up with one on your own, here is an example:
from math import isqrt
limit = 40
primes = [True]*limit
primes[0] = False
primes[1] = False
for i in range(2,isqrt(limit-1)+1):
if primes[i]:
for j in range(i*i,limit,i):
primes[j] = False
from numpy import where
print(list(where(primes)[0]))
# >>> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]

Is this algorithm running on O(n) or O(n^2) for sliding window?

class Solution:
def maximum_of_all_subarrays_of_size_k(self,arr,k):
j = k
overall_max = float('-inf')
for i in range(0, len(arr)-k+1):
sub_arr = arr[i:j]
print(f"maximum of current {sub_arr} is {max(sub_arr)}")
current_max = max(sub_arr)
overall_max = max(overall_max, current_max)
j+=1
return overall_max
if __name__ == "__main__":
sol = Solution()
arr = [8, 5, 10, 7, 9, 4, 15, 12, 90, 13]
k = 3
print(sol.maximum_of_all_subarrays_of_size_k(arr,k))
Would like to know if I'm correct in assuming the run time to be 0(n), what's confusing me is the array slicing part.
With arr[i:j] this code takes all slices of the list that have size 𝑘. Taking a slice has a O(𝑘) time complexity, where 𝑘 is the size of the slice. Also performing max on that slice is O(𝑘).
There are 𝑛 − 𝑘 such slices, so the overall time complexity is O((𝑛 − 𝑘)𝑘).
If you want to exclude the 𝑘 factor from this, then note that for a very small 𝑘 this is about O(𝑛), and when 𝑘 is close to 𝑛 then also this is about O(𝑛). But when 𝑘 is like 𝑛/2, we get O((𝑛 − 𝑛/2)𝑛/2) = O(𝑛²). So that would be the worst case time complexity in terms of 𝑛 only.

how can I increment the first number in a range?

Learning python and I need to figure out to to increase the first number in a range?
measures = 4
beats_per_measures = 4
beats = 0
for i in range(0, measures):
for beats in range(1 , beats_per_measure + 1):
print(beats)
so I can get the output to print the beats per measure the number of measures. BOOm. but now I need to increase the first beat by one every measure and I know it's easier than I am making it. I have preformed multiple searches online and I am guessing that because I don't know how to read the complex answers or I don't know how to phrase what I am trying to do is why I have not found an answer yet.
Is this what you need?
I thought you needed each set (1,2,3,4) in the same line.
measures = 4
beats_per_measure = 4
for i in range(measures):
for var_beat in range(beats_per_measure):
var_beat = str(1 + var_beat)
fixed_beat = ' '.join([ str(1+f) for f in range(beats_per_measure)[1:]])
print(var_beat + ' ' + fixed_beat)
I am a little bit confused with your "so on" in your comment.
You can do:
for e in range(1,5):
print(', '.join(map(str, ([e]+list(range(2,5))))))
Prints:
1, 2, 3, 4
2, 2, 3, 4
3, 2, 3, 4
4, 2, 3, 4
Or, perhaps:
>>> print('\n'.join([f"{e}, 2, 3 ,4" for e in range(1,5)]))
# same output
Which could be made dynamic by this:
measures = 4
beats_per_measures = 4
rest_of_measure=', '.join(f'{e}' for e in range(2,beats_per_measures+1))
print('\n'.join([f"{e}, {rest_of_measure}" for e in range(1, measures+1)]))
From your comment, you just want to always print an increasing number followed by 2, 3, 4. So just print the first beat manually based on the measure you're currently processing:
for measure in range(1, measures+1): # Adjust bounds so we don't have to add one inside the loop
print(measure) # Prints the changing value
for beats in range(2, beats_per_measure + 1):
print(beats) # Prints the consistent 2, 3, 4 values
If you want each measure on the same line, the minimal change is to just add end='' or end=' ' to each print and add an empty print() to the end of the outer loop.
A cleaner solution would be to just print each measure all at once; you can unpack a range argument with * to make it operate as separate arguments to print, and use sep to provide the characters you want in between, getting something like:
for measure in range(1, measures + 1):
print(measure, *range(2, beats_per_measure + 1), sep=', ')
# prints:
1, 2, 3, 4
2, 2, 3, 4
3, 2, 3, 4
...
and it's trivial to change the separation to just a single space, sep=' ', or no separation at all, sep=''.
measures = 4
beats_per_measure = 4
for measure in range(0 , measures):
for beats in range(1 , beats_per_measure + 1):
#print(beats)
if beats == 1:
beats += measure
elif beats != 1:
beats = beats
print(beats)
this is the answer I was looking for. It was a ^%^%$ conditional that helped me do it!!! Thanks for all the help can't wait to plug in all of your recommendations to see what else I can do!

Refactor a hash table solution to twoSum problem

I try the most to solve a twoSum problem in leetcode
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
The plan:
1) brute force to iterate len(nums) O(n)
2) search for target - num[i] with a hash table O(1)
Implement
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
nums_d = {}
for i in range(len(nums)):
nums_d.setdefault(nums[i], []).append(i)
for i in range(len(nums)):
sub_target = target - nums[i]
nums_d[nums[i]].pop(0) #remove the fixer
result = nums_d.get(sub_target)#hash table to search
if result:
return [i, result[0]]
return []
I strives hours for this solution but found that answer accepted but not passed Score 60.
Runtime: 60 ms, faster than 46.66% of Python3 online submissions for Two Sum.
Memory Usage: 16.1 MB, less than 5.08% of Python3 online submissions for Two Sum.
I want to refactor the codes so that to achieve at least faster than 60%.
Could you please provide hints?
Approach 3 in the "Solution" tab in the problem on LeetCode seems to be faster than 80%. Here's some incomplete code for you to fill in:
def twoSum(self, nums: List[int], target: int) -> List[int]:
map = # ???
for i in range(len(nums)):
complement = # ???
if complement in map:
return # ???
map[nums[i]] = # ???

How to get different answers from different threads?

To get to know threading concept better I tried to use threads in a simple program. I want to call a function 3 times which does random selection.
def func(arg):
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
num = random.choice(lst)
arg.append(num)
return arg
def search(arg):
a = func(arg)
a = func(a)
threads_list = []
que = queue.Queue()
for i in range(3):
t = threading.Thread(target=lambda q, arg1: q.put(func(arg1)), args=(que, a))
t.start()
threads_list.append(t)
for t in threads_list:
t.join()
while not que.empty():
result = que.get()
print (result)
if __name__ == '__main__':
lst = []
search(lst)
As you can see In the third part, I used threads but I expected to get different lists ( different for the third part).
but all the threads return the same answer.
Can anyone help me to get different lists from different threads?
I think I misunderstood the concept of multiprocessing and multithreading.
Possibly, the pseudo-random number generator which random.choice is using is using three instances - one for each thread - and in the absence of a unique seed will produce the same pseudo-random sequence. Since no seed is provided, it may be using the system time which, depending on the precision, may be the same for all three threads.
You might try seeding the PRNG with something that will differ from thread to thread, inside the thread that invokes the PRNG. This should cause the three threads to use different seeds and give you different pseudo-random sequences.

Resources