Game of life is running slow - nim-lang

I am trying to simulate n-dimensional game of life for first t=6 time steps. My Nim code is a straightforward port from Python and it works correctly but instead of the expected speedup, for n=4, t=6 it takes 2 seconds to run, which is order of magnitude slower than my CPython version. Why is my code so slow? What can I do to speed it up? I am compiling with -d:release and --opt:speed
I represent each point in space with a single 64bit integer.
That is, I map (x_0, x_1, ..., x_{n-1}) to sum x_i * 32^i. I can do that since I know that after 6 time steps each coordinate -15<=x_i<=15 so I have no overflow.
The rules are:
alive - has 2 or 3 alive neigbours: stays alive
- different number of them: becomes alive
dead - has 3 alive neighbours: becomes alive
- else: stays dead
Below is my code. The critical part is the proc nxt which gets set of active cells and outputs set of active cells next time step. This proc is called 6 times. The only thing I'm interested in is the number of alive cells.
I run the code on the following input:
.##...#.
.#.###..
..##.#.#
##...#.#
#..#...#
#..###..
.##.####
..#####.
Code:
import sets, tables, intsets, times, os, math
const DIM = 4
const ROUNDS = 6
const REG_SIZE = 5
const MAX_VAL = 2^(REG_SIZE-1)
var grid = initIntSet()
# Inits neighbours
var neigbours: seq[int]
proc initNeigbours(base,depth: int) =
if depth == 0:
if base != 0:
neigbours.add(base)
else:
initNeigbours(base*2*MAX_VAL-1, depth-1)
initNeigbours(base*2*MAX_VAL+0, depth-1)
initNeigbours(base*2*MAX_VAL+1, depth-1)
initNeigbours(0,DIM)
echo neigbours
# Calculates next iteration:
proc nxt(grid: IntSet): IntSet =
var counting: CountTable[int]
for x in grid:
for dx in neigbours:
counting.inc(x+dx)
for x, count in counting.pairs:
if count == 3 or (count == 2 and x in grid):
result.incl(x)
# Loads input
var row = 0
while true:
var line = stdin.readLine
if line == "":
break
for col in 0..<line.len:
if line[col] == '#':
grid.incl((row-MAX_VAL)*2*MAX_VAL + col-MAX_VAL)
inc row
# Run computation
let time = cpuTime()
for i in 1..ROUNDS:
grid = nxt(grid)
echo "Time taken: ", cpuTime() - time
echo "Result: ", grid.len
discard stdin.readLine

Your code runs in my computer in about 0.02:
Time taken: 0.020875947
Result: 2276
Time taken: 0.01853268
Result: 2276
Time taken: 0.021355269
Result: 2276
I changed the part where the input is read to this:
# Loads input
var row = 0
let input = open("input.txt")
for line in input.lines:
for i, col in line:
if col == '#':
grid.incl((row-MAX_VAL)*2*MAX_VAL + i-MAX_VAL)
inc row
input.close()
But it shouldn't impact the performance, it just looks better to my eyes. I compiled with:
nim c -d:danger script.nim
Using Nim 1.4.2. -d:danger is the flag for maximum speed before entering deeper waters.
But even compiling in debug mode:
$ nim c -r script.nim
Time taken: 0.07699487199999999
Result: 2276
Way faster than 2 seconds. There has to be other problem in your end. Sorry for the non-answer.

Related

Find the sum of the digits using a loop and a function

I am using a function to get the number from user, and I am trying to use a while loop to separate the digits of a number. And I am trying to add the digits of the number. But my code runs infinitely.
Example : 2345 -> 15
def sumDigits(n):
sum=0
while len(str(n))>0:
a = n%10
n = n//10
sum += a
return sum
print(sumDigits(2345))
Expected: 15
Actual: I had to shut down the jupyter kernel to stop the while loop.
Edit 2: Removed the updated code as it was answered by the community.
This condition len(str(n))>0 can never be false as long as n is an integer, because str(0) is '0', which has a length of 1.
You need to change the looping condition to exit where there is no more digit to sum, which happens when n reaches 0:
def sum_digits(n):
total = 0
while n > 0:
a = n % 10
n = n // 10
total += a
return total
print(sum_digits(2345))
Note: sum is a built-in in python, so naming a variable sum is not advised. Also, method names usually are written in snake_case, so sum_digits is recommended.
def all_sum(number):
total = 0
if number > 0:
for e in str(number):
if e.isdigit():
total += int(e)
else:
pass
return total
a = all_sum(567897)
This should do your work. Instead of doing two arithmetic operations to 'fetch' the digits, better to change the argument to string and just use each digit. Its faster and saves memory (though it's not too memory consuming).

I am getting a "Time Limit Exceeded " error in the following code. How to fix that error

The following code is my view of checking whether the sum of a number and it's reverse is a palindrome or not.If the sum is a palindrome then sum will be displayed.Otherwise the process will be repeated until we get a palindrome. When it is set to execute, I am getting a time limit exceeded error.Where do I need to correct the code?
def pal(n1):
temp=n1
rev=0
while(temp>0):
rev=(rev*10)+(temp%10)
temp=temp/10
sum1=n1+rev
temp=sum1
rev=0
while(temp>0):
rev=(rev*10)+(temp%10)
temp=temp/10
if(rev==sum1):
print(sum1)
else:
pal(sum1)
n=int(input())
pal(n)
I expect the output of a number 453 to be 6666.
i.e.
453+354=807 (not a palindrome. So repeat the process)
807+708=1515
1515+5151=6666 (it is a palindrome)
Your problem is that you are checking for while temp > 0: but inside that loop you are using float division: temp=temp/10. So the condition will always hold. For example:
>>> 8/10
0.8
>>> 0.8/10
0.08
What you want is to change your divisions to int division:
>>> 8//10
0
Still you might consider working with strings which is much easier in that case:
def pal(n):
rev_n = str(n)[::-1]
sum_str = str(n + int(rev_n))
while sum_str != sum_str[::-1]:
# print(sum_str)
sum_rev = sum_str[::-1]
sum_str = str(int(sum_str) + int(sum_rev))
print(sum_str)
And with the commented print this gives:
>>> pal(453)
807
1515
6666
Here is one way of doing this using string manipulation, which goes a lot easier than trying to do this with numbers. It is also a more direct translation of what you describe afterwards. (I do not really see the link between your code and your description...)
def is_palindrome(text):
# : approach1, faster for large inputs
# mid_length = len(text) // 2
# offset = 0 if len(text) % 2 else 1
# return text[:mid_length] == text[:-mid_length - offset:-1]
# : approach2, faster for small inputs
return text == text[::-1]
def palindrome_sum(num):
while not is_palindrome(num):
num = str(int(num) + int(num[::-1]))
return num
num = input() # 453
palindrome = palindrome_sum(num)
print(palindrome)
# 6666

Why isn't this function entering the while loop?

def millerRabin(toTest = 3, accuracy = 5):
# Return true if toTest is prime
################################
# Find how many times toTest can be halved
print(toTest)
under = toTest - 1
loopTracker = 0
while under % 2 == 0:
print('Before halving')
# Keep halving, and keep track until hit we can no longer divide in half
under = under // 2
print('After Halving: ', under)
loopTracker += 1
print("looped")
print(loopTracker)
print(millerRabin(toTest = 144000))
The first portion of the Miller-Rabin is to track how many times the number to be tested can be halved. But, I can't figure out why the program is not entering the while loop and outputting some of the print statements.
When you define under, you're subtracting 1. This creates 143999 to test, which is not divisible by 2 evenly. So it fails your while condition and never enters the loop.

How do I make a program that asks the user for a limit and

I've got this code that asks the user for a limit, and then prints out the sequence of square numbers that are less than or equal to the limit provided.
n=int(input("Limit: "))
counter = 2
while counter <= n:
a = counter*counter
counter=a
print(a)
This is my current code, it's meant to work like this:
Max: 100
1
4
9
16
25
36
49
64
81
100
I'm stuck, how do I fix it? Thanks!
First off, you will want to start your counter variable at 1, otherwise you won't be able to get '1' as a square value.
In terms of printing the rest of the values, you will need to do 3 things:
Check if the square of counter is less than the limit.
If it is than the limit, print the result of counter * counter.
Increment counter by 1 <-- This is important!
Incrementing the counter by 1, is going to allow you to check every possible square that could exist below the specified limit. The following code provides a simple way to accomplish this that matches the pseudocode above:
n=int(input("Limit: "))
counter = 1
while counter <= n:
if counter * counter <= n:
print(counter * counter)
counter += 1
Let me know if you have any questions, I'm happy to clarify anything that still isn't making sense!
You're not actually calculating successive squares. You should be finding the square of counter, and then increasing counter by one
n=int(input("Limit: "))
counter = 1
sq = counter**2
while sq <= n:
print(sq)
counter += 1
sq = counter**2
Fun itertools solution:
from itertools import accumulate, count, takewhile
for i in takewhile(n.__gt__, accumulate(count(1, 2))):
print(i)

Beginner Python: Where to "while"?

tl;dr: My code "works", in that it gives me the answer I need. I just can't get it to stop running when it reaches that answer. I'm stuck with scrolling back through the output.
I'm a complete novice at programming/Python. In order to hone my skills, I decided to see if I could program my own "solver" for Implied Equity Risk Premium from Prof. Damodaran's Valuation class. Essentially, the code takes some inputs and "guesses and tests" a series of interest rates until it gets a "close" value to the input.
Right now my code spits out an output list, and I can scroll back through it to find the answer. It's correct. However, I cannot for the life of me get the code to "stop" at the correct value with the while function.
I have the following code:
per = int(input("Enter the # of periods forecast ->"))
divbb = float(input("Enter the initial dividend + buyback value ->"))
divgr = float(input("Enter the div + buyback growth rate ->"))
tbondr = float(input("Enter the T-Bond rate ->"))+0.000001
sp = int(input("Enter the S&P value->"))
total=0
pv=0
for i in range(1,10000):
erp = float(i/10000)
a = divbb
b = divgr
pv = 0
temppv = 0
print (sp-total, erp)
for i in range(0, per):
a=a * (1+b)
temppv = a / pow((1+erp),i)
pv=pv+temppv
lastterm=(a*1+tbondr)/((erp-tbondr)*pow(1+erp,per))
total=(pv+lastterm)
From his example, with the inputs:
per = 5
divbb = 69.46
divgr = 0.0527
tbondr = 0.0176
sp = 1430
By scrolling back through the output, I can see my code produces the correct minimum at epr=0.0755.
My question is: where do I stick the while to stop this code at that minimum? I've tried a lot of variations, but can't get it. What I'm looking for is, basically:
while (sp-total) > |1|, keep running the code.
per = 5
divbb = 69.46
divgr = 0.0527
tbondr = 0.0176
sp = 1430
total=0
pv=0
i = 1
while(abs(sp-total)) > 1:
erp = i/10000.
a = divbb
b = divgr
pv = 0
temppv = 0
print (sp-total, erp)
for j in range(0, per):
a=a * (1+b)
temppv = a / pow((1+erp),j)
pv=pv+temppv
lastterm=(a*1+tbondr)/((erp-tbondr)*pow(1+erp,per))
total=(pv+lastterm)
i += 1
should work. Obviously, there are a million ways to do this. But the general gist here is that the while loop will stop as soon as it meets the condition. You could also test every time in the for loop and include a break statement, but because you don't know when it will stop, I think a while loop is better in this case.
Let me give you a quick rundown of two different ways you could solve a problem like this:
Using a while loop:
iterator = start value
while condition(iterator):
do some stuff
increment iterator
Using a for loop:
for i in xrange(startvalue, maxvalue):
do some stuff
if condition:
break
Two more thing: if you're doing large ranges, use the generator xrange. Also, it's probably a bad idea to reuse i inside your for loop.
I recommend CS101 from Udacity.com for learning Python. Also, if you're interested in algorithms, work through the problems at projecteuler.com

Resources