I wrote this code. But it works very slowly.
I'm figuring out how many times I have to run the case generator to find numbers less than or equal to inv, in this case six. I count the number of attempts until a digit <= 6 is generated. I find inv equal to 1 and repeat the loop. Until inv is 0. I will keep trying to generate six digits <= 6.
And I will repeat all this 10 ** 4 degrees again to find the arithmetic mean.
Help me speed up this code. Works extremely slowly. The solution should be without third-party modules. I would be immensely grateful. Thank!
import random
inv = 6
def math_count(inv):
n = 10**4
counter = 0
while n != 0:
invers = inv
count = 0
while invers > 0:
count += 1
random_digit = random.randint(1, 45)
if random_digit <= invers:
invers -= 1
counter += count
count = 0
if invers == 0:
n -= 1
invers = inv
return print(counter/10**4)
math_count(inv)
1 Answer 1
The code is very hard to follow. Here's a possible refactor to make its intent clearer. Some key ideas in the simplification: (1) use loops to manage counters rather than manually incrementing/decrementing; (2) use more meaningful variable names when context isn't clear; and (3) delegate some of the complexity to a helper function. The third idea is the most impactful: the helper function is simple because it does so little; and the main function, having been relieved of a burden, is simpler as well -- so simple, in fact, that it boils down to computing a sum over two loops.
def math_count(inv, trials = 10000):
total_attempts = sum(
attempts_to_get_lte(x)
for _ in range(trials, 0, -1)
for x in range(inv, 0, -1)
)
return total_attempts / trials
def attempts_to_get_lte(x):
attempts = 0
while True:
attempts += 1
if random.randint(1, 45) <= x:
return attempts
But attempts_to_get_lte()
is an easily solved probability problem: rather
than have Python simulate the manual flipping of coins, we can just do a little
math. If you prefer that approach, I believe the following is correct
(probability experts should chime in if I've gone astray):
def math_count_exact(inv):
return sum(45 / x for x in range(inv, 0, -1))
-
\$\begingroup\$ We need a simulation. Simulation gives more accurate calculations. Thanks for your code. I tested it, but it still works slowly, I already don’t know how to speed it up. \$\endgroup\$Pythonist– Pythonist2020年09月24日 11:03:56 +00:00Commented Sep 24, 2020 at 11:03
-
\$\begingroup\$ @Pythonist Correct, the edited
math_count()
won't be any faster. Currently it takes about 1 sec. A key question is how much faster? The answer – a little bit faster or an order of magnitude faster – affects which strategies are viable. \$\endgroup\$FMc– FMc2020年09月24日 16:01:11 +00:00Commented Sep 24, 2020 at 16:01 -
1\$\begingroup\$ @Pythonist it'd help if you add in the question what you're trying to simulate? maybe there might be entirely different route to take? \$\endgroup\$hjpotter92– hjpotter922020年09月24日 16:09:00 +00:00Commented Sep 24, 2020 at 16:09
-
\$\begingroup\$ @hjpotter92 I have 10 digits out of 45. inv = 10. need to calculate how many random number generations need to make on average to get 10 times the number <= inv. Each time the number is found, the inv decreases by 1. \$\endgroup\$Pythonist– Pythonist2020年09月24日 16:37:32 +00:00Commented Sep 24, 2020 at 16:37
def math_count(inv, trials = 10000): return sum(attempts_to_get_lte(x) for n in range(trials, 0, -1) for x in range(inv, 0, -1)) / trials
, wheretrials = 10000
andattempts_to_get_lte()
is a simple function that computes how many attempts are needed untilrandom.randint(1, 45) <= x
. So, one answer to the OP's question is Use fewer trials for the estimate. Another answer, as you note, is Use math to get the answer immediately. \$\endgroup\$