Ruby 如何更快地解决euler#21项目?

Ruby 如何更快地解决euler#21项目?,ruby,Ruby,原始问题 设d(n)定义为n的适当因子之和(小于n的数平均分成n)。 如果d(a)=b和d(b)=a,其中a b,那么a和b是友好对,a和b中的每一个都称为友好数 例如,220的适当除数为1、2、4、5、10、11、20、22、44、55和110;因此d(220)=284。284的适当除数为1、2、4、71和142;所以d(284)=220 计算10000以下所有友好数字的总和 我通过生成1-10000之间的所有数字及其相应的除数和的散列来解决这个问题(即散列[220]=284)。然后我将散列中

原始问题

设d(n)定义为n的适当因子之和(小于n的数平均分成n)。 如果d(a)=b和d(b)=a,其中a b,那么a和b是友好对,a和b中的每一个都称为友好数

例如,220的适当除数为1、2、4、5、10、11、20、22、44、55和110;因此d(220)=284。284的适当除数为1、2、4、71和142;所以d(284)=220

计算10000以下所有友好数字的总和

我通过生成1-10000之间的所有数字及其相应的除数和的散列来解决这个问题(即散列[220]=284)。然后我将散列中的项目与散列的副本进行比较。。。不管怎样,这是可行的,但需要很长时间。我怎样才能使它更快

def proper_divs_sum num
  divs = [1]
  for i in 2..((num/2) + 1)
    if num % i == 0
      divs.push i
    end
  end

  divs_sum = 0
  divs.each do |div|
    divs_sum += div
  end
  return divs_sum
end

def n_d_hash_gen num
  nd_hash = {}
  for i in 1..num
    nd_hash[i] = proper_divs_sum(i)
  end
  return nd_hash
end

def amicables num
  amicable_list = []
  hash1 = n_d_hash_gen(num)
  hash2 = n_d_hash_gen(num)

  hash1.each do |item1|
    hash2.each do |item2|
      if item1 != item2 && (item1[0] == item2[1] && item2[0] == item1[1])
        amicable_list.push item1
      end
    end
  end
  return amicable_list
end

另外,我对Ruby还不熟悉,所以任何关于如何使它更像Ruby的提示都将非常感谢。

你可以“作弊”并使用Ruby的stdlib prime:除法是一个缓慢的过程。在你的方法中,你做了很多,因此你的程序很慢

首先,在试图找到一个数的所有除数时,你正在尝试所有不大于该数一半的除数作为潜在除数。您可以通过不超过数字的平方根来改进这一点。如果一个数可以被一个大于其平方根的数整除,则整除的结果将小于平方根。这将消除一些不必要的分歧

此外,如果一个数字不能被2整除,那么它也不能被4、6、8等整除。最好只被素数整除,然后根据这些素数构建可能的除数


但是,完全不进行除法可以解决问题。

您可以做几件事来改进算法:

1) 计算除数时,不需要循环到n/2。停在sqrt(2)处。到那时你已经找到了一半的除数;另一半计算为n除以前一半

2) 当您在哈希表中输入一个数字时,您可以立即检查它的友好孪生兄弟是否已经在哈希表中。不需要两个哈希表,也不需要两个嵌套循环来比较它们。

分析您的方法 您所采用的方法是从除法开始,找到它的除数,求和并存储它们。你会注意到你用来寻找除数的方法很幼稚——我不认为这是一种侮辱;这只是说你的方法没有使用任何可用的信息,只是尝试每一个数字,看看它是否是一个除数。它通过使用模块化除法来实现这一点,而且,在几乎所有情况下,大多数考生都没有通过考试

更具建设性的东西 考虑一下,如果你从未尝试过可能会在这样的测试中失败的数字。事实上,从除数开始并从除数中获得红利将完全回避这个问题

你可以通过循环每个数字来实现这一点函数d(n)(通常称为σ(n))是的一个变体,它有一个重要的特性,可以让你更有效地计算它。它是a,这意味着如果n=ab,其中a和b是互质,那么d(n)=d(a)d(b)

这意味着如果你能计算出d(pk),其中p是素数,那么d(n)=d(p1k1)。。。d(prkr),其中n=p1k1…prkr是n的素因式分解。事实上,结果是d(pk)=(pk+1-1)/(p-1),所以d(n)=∏i(piki+1-1)/(pi-1)

因此,对于所有1&leq,有效地计算d(n);n≤10000,您可以使用筛子计算所有n的素因子分解,然后使用上面的公式使用素因子分解计算d(n)

完成后,只需要一个简单的循环来计算所有n的和,其中d(d(n))=n


这甚至可以通过将筛选步骤与d(n)的计算相结合来进一步优化,但我将把它留给感兴趣的人作为练习。对于这个特定问题的大小,这是不必要的。

Java中的另一个解决方案:

static int sum_Of_Divisors(int n){
    int limit = n;
    int sum = 0;
    for(int i=1;i<limit;i++){
        if(n%i==0){
            if(i!=1)
                sum += (i + n/i);
            else 
                sum += i;
            limit = n/i;
        }
    }       
    return sum;
}

static boolean isAmicable(int n, HashSet<Integer> set){
    int sum = sum_Of_Divisors(n);   
    if(sum_Of_Divisors(sum)==n && n!=sum){
        set.add(sum);
        return true;
    }
    return false;
}

static long q21(){
    long sum = 0;
    HashSet<Integer> set = new HashSet<Integer>();
    for(int i=1;i<10000;i++){
        if(!set.contains(i)){
            if(isAmicable(i,set)){
                set.add(i);
            }
        }
    }
    for(Integer i: set) sum+=i;
    return sum;
}
除数的静态整数和(整数n){
int极限=n;
整数和=0;

对于(int i=1;我想ProjectEuler不会在您证明自己已经解决了问题后为您提供其他解决方案吗?那里什么都没有吗?至于您的问题,可能有一种使用10000x1000数组的动态编程方法。pyeuler()在大约0.3秒内解决了这个问题,所以我想你应该能够在Ruby中获得类似的时间。无论如何,也许这应该移动到?在我解决了这个问题之后,没有给出其他解决方案。我将研究10000x1000数组方法,但我很好奇,我解决它的方式是否有任何改变,可以提高它的速度。我尝试了适当的改变_divs_sum从2开始测试-数字sqrt:
对于2中的i..Math.sqrt(num)
但现在它只返回nil。我缺少什么吗?这只是一个小优化;这个问题需要的是一个范式转换到一个更具建设性的方法