4

I started learning Nim yesterday and decided to code a little test to make a performance comparison with Rust. The code was fairly easy to write and works for values up to 10^9. However, I need to test it with at least 10^12, which gives incorrect values because of an overflow, even while using uint.

I've been trying different conversions for most variables but I can't seem to avoid the overflow. Of course, any suggestions to make the code easier to read are more than welcome!

import math
import sequtils
import unsigned

proc sum_min_pfactor(N : uint) : uint =
    proc f(n : uint) : uint =
        return n*(n+1) div 2 - 1
    var
        v = int(math.sqrt(float(N)))
        used = newSeqWith(v+1,false)
        s_sum,s_cnt,l_cnt,l_sum = newSeq[uint](v+1)
        ret = 0.uint
    for i in -1..v-1:
        s_cnt[i+1] = i.uint
    for i in 0..v:
        s_sum[i] = f(i.uint)
    for i in 1..v:
        l_cnt[i] = N div i.uint - 1
        l_sum[i] = f(N div i.uint)
    for p in 2..v:
        if s_cnt[p] == s_cnt[p-1]:
            continue
        var p_cnt = s_cnt[p - 1]
        var p_sum = s_sum[p - 1]
        var q = p * p
        ret += p.uint * (l_cnt[p] - p_cnt)
        l_cnt[1] -= l_cnt[p] - p_cnt
        l_sum[1] -= (l_sum[p] - p_sum) * p.uint
        var interval = (p and 1) + 1
        var finish = min(v,N.int div q)
        for i in countup(p+interval,finish,interval):
            if used[i]:
                continue
            var d = i * p
            if d <= v:
                l_cnt[i] -= l_cnt[d] - p_cnt
                l_sum[i] -= (l_sum[d] - p_sum) * p.uint
            else:
                var t = N.int div d
                l_cnt[i] -= s_cnt[t] - p_cnt
                l_sum[i] -= (s_sum[t] - p_sum) * p.uint
        if q <= v:
            for i in countup(q,finish-1,p*interval):
                used[i] = true
        for i in countdown(v,q-1):
            var t = i div p
            s_cnt[i] -= s_cnt[t] - p_cnt
            s_sum[i] -= (s_sum[t] - p_sum) * p.uint
    return l_sum[1] + ret

echo(sum_min_pfactor(uint(math.pow(10,2))))
Nicolás Siplis
  • 71
  • 3
  • 16

2 Answers2

4

How do you solve it in Rust? Rust's ints should also be 64bit at most. In your f function it gets a bit difficult when n is 10000000000. You have a few choices:

Some stylistic changes:

import math

proc sum_min_pfactor(N: int): int =
  proc f(n: int): int =
    n*(n+1) div 2 - 1

  var
    v = math.sqrt(N.float).int
    s_cnt, s_sum, l_cnt, l_sum = newSeq[int](v+1)
    used = newSeq[bool](v+1)

  for i in 0..v: s_cnt[i] = i-1
  for i in 1..v: s_sum[i] = f(i)
  for i in 1..v: l_cnt[i] = N div i - 1
  for i in 1..v: l_sum[i] = f(N div i)

  for p in 2..v:
    if s_cnt[p] == s_cnt[p-1]:
      continue

    let
      p_cnt = s_cnt[p - 1]
      p_sum = s_sum[p - 1]
      q = p * p

    result += p * (l_cnt[p] - p_cnt)
    l_cnt[1] -= l_cnt[p] - p_cnt
    l_sum[1] -= (l_sum[p] - p_sum) *  p

    let interval = (p and 1) + 1
    let finish = min(v,N div q)

    for i in countup(p+interval,finish,interval):
      if used[i]:
        continue
      let d = i * p
      if d <= v:
        l_cnt[i] -= l_cnt[d] - p_cnt
        l_sum[i] -= (l_sum[d] - p_sum) * p
      else:
        let t = N div d
        l_cnt[i] -= s_cnt[t] - p_cnt
        l_sum[i] -= (s_sum[t] - p_sum) * p

    if q <= v:
      for i in countup(q,finish-1,p*interval):
        used[i] = true

    for i in countdown(v,q-1):
      let t = i div p
      s_cnt[i] -= s_cnt[t] - p_cnt
      s_sum[i] -= (s_sum[t] - p_sum) * p

  result += l_sum[1]

for i in 2..12:
  echo sum_min_pfactor(int(math.pow(10,i.float)))
def-
  • 5,275
  • 20
  • 18
  • I haven't started the solution in Rust yet, I decided to start with Nim first. I actually managed to use uint in my code after some struggling, but the fact that it's just short of the range I need is frustrating. So neither language has native support for big integers/128-bit integers? – Nicolás Siplis Jul 10 '15 at 19:55
  • I added a few suggestions, including bigints and 128bit ints. But your performance will take a big hit. 128bit ints or GMP might work well. – def- Jul 10 '15 at 20:00
  • Excellent, just what I needed! I'll take a look at the 128-bit ints implementation, never had to deal with GMP but if the difference is significant I might play a bit with the big integer module. – Nicolás Siplis Jul 10 '15 at 20:06
2

Please also take a look at the bignum package: https://github.com/FedeOmoto/bignum

It's a higher level wrapper around nim-gmp so you don't have to deal with low level stuff like the different programming models (GMP uses long C type extensively, so it's a bit troublesome when targeting Win64 - LLP64).