【问题标题】:Express any number as the sum of four prime numbers将任意数表示为四个素数之和
【发布时间】:2011-02-14 04:59:33
【问题描述】:

我遇到了一个问题,将任何数字表示为四个素数之和。

条件:

  • 不允许使用任何类型的数据库。
  • 最长执行时间:3 秒
  • 数字只到 100,000
  • 如果无法拆分,则返回 -1

我做了什么:

  1. 使用 Eratosthenes 的筛子,我计算了所有素数,直到指定的数字。

  2. 查找了一个称为哥德巴赫猜想的概念,该猜想将偶数数表示为两个素数的总和。

但是,我被困在这之外。 任何人都可以帮助我了解您可能采取的方法吗?

Eratosthenes 的筛子需要两秒钟来计算最多 100,000 个素数。

【问题讨论】:

  • 这显然对所有数字
  • 来源:Creative Exersise 39,Java 编程简介,cs.princeton.edu/introcs/42sort
  • 执行时间不仅仅与算法本身有关。它还取决于执行算法的机器。
  • 我喜欢 LINQ;可惜它必须是c ++。 =) var answer = from p in primes from q in primes from r in primes from s in primes where p + q + r + s == n where p <= q && q <= r && r <= s select new { p, q, r, s };
  • 两秒对于筛子来说听起来有点太长了。要么你的机器特​​别慢,要么你做错了什么。

标签: c++ algorithm math


【解决方案1】:

您的筛子实施存在一些严重问题。我刚刚在 Delphi 中实现了它,在主频为 2.93 GHz 的 Intel Core i7 CPU 上,所需时间不到一毫秒(0.37 毫秒)!

我使用了以下代码:

program Sieve;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

const
  N = 100000;

var
  i, j: integer;
  tc1, tc2, freq: Int64;
  Arr: packed array[2..N] of boolean;

begin

  FillChar(Arr, length(Arr) * sizeof(boolean), 1);

  QueryPerformanceFrequency(freq);

  QueryPerformanceCounter(tc1);
  for i := 2 to N div 2 do
    if Arr[i] then
    begin
      j := 2*i;
      while j <= N do
      begin
        Arr[j] := false;
        inc(j, i);
      end;
    end;

  QueryPerformanceCounter(tc2);
  Writeln(FloatToStr((tc2 - tc1)/freq));

  Writeln;

  for i := 2 to 100 do
    Writeln(IntToStr(i) + ': ' + BoolToStr(arr[i], true));

  Writeln('...');

  Readln;

end.

【讨论】:

    【解决方案2】:

    这是一个 PHP 实现,在 n = 99999 时运行时间不到 2 秒:

    function Eratosthenes($number)
    {
        static $primes = array();
    
        if (empty($primes[$number]) === true)
        {
            $sqrt = sqrt($number);
            $primes[$number] = array_merge(array(2), range(3, $number, 2));
    
            for ($i = 2; $i <= $sqrt; ++$i)
            {
                foreach ($primes[$number] as $key => $value)
                {
                    if (($value != $i) && ($value % $i == 0))
                    {
                        unset($primes[$number][$key]);
                    }
                }
            }
        }
    
        return $primes[$number];
    }
    
    $time = microtime(true);
    $number = 99995;
    $primes = array();
    
    if ($number % 2 == 0)
    {
        $primes = array(2, 2);
    }
    
    else
    {
        $primes = array(2, 3);
    }
    
    $number -= array_sum($primes);
    
    foreach (Eratosthenes($number) as $prime)
    {
        if (in_array($number - $prime, Eratosthenes($number)))
        {
            $primes[] = $prime;
            $primes[] = $number - $prime;
    
            die(implode(' + ', $primes) . ' (' . (microtime(true) - $time) . ')');
        }
    }
    

    当然,它不适用于 8 以下的数字,除非您(错误地)将 1 视为质数。

    【讨论】:

      【解决方案3】:

      就 Sieve 的主题而言,这是我认为未优化的“愚蠢”实现:

      std::vector<int> primes(int n) {
          std::vector<int> s(n+1);
          for (int i = 2; i * i <= n; ++i) {
              if (s[i] == 0) {
                  for (int j = 2*i; j <= n; j += i) {
                      s[j] = 1;
                  }
              }
          }
      
          std::vector<int> p;
          for (int i = 2; i <= n; ++i) {
              if (s[i] == 0) {
                  p.push_back(i);
              }
          }
          // std::copy(p.begin(), p.end(), std::ostream_iterator<int>(std::cout, ", "));
          return p;
      }
      

      对我来说,它在几分之一秒内运行(n=100000)。

      【讨论】:

        【解决方案4】:

        您可以通过注意一个简单的事实来缩小所需的搜索范围:当您将两个数字相加时,和的最后一位将是两个数字的最后一位之和的最后一位。例如 2345 + 24323 = 26668 和 5+3=8;如果最后一位数字总和为 2 位数字,则使用其最后一位数字,例如。 2345+5436=7781 5+6=11,最后一位是1。

        所以,按照前面建议的算法:

        1. 使用埃拉托色尼筛法计算所有小于 N 的素数。
        2. 将两个素数之和的列表制成表格。
        3. 根据最后一位分为 10 个 std::set 框
        4. 查看您号码的最后一位,找到 可以弥补的组合(包括进位)。使用这些来限制搜索范围

        例如,

        1. 对于像34565这样的数字,最后一位是5,分量来自(0,5),(1,4),(2,3),(3,2),(4,1 ),(5,0),(6,9),(7,8),(8,7),(9,6)。排除重复项,我们剩下 (0,5), (1,4), (2,3), (6,9), (7,8)。 (预先计算所有最后一位数字和 硬编码到您的程序中)。

        2. 如果 N 是原始数字,则从“0”框中选择每个数字 M,检查 (N-M) 是否是“5”框的成员等,以获取所有可能的组合。如果是这样,您已经找到了答案!

          • Sreenadh

        【讨论】:

        • "排除重复,我们剩下 (0,5), (1,4), (2,3), (6,9), (7,8)", 排除非- 我们只剩下 (2,3) 的素数 - 这是一个很重要的说明,一个简单的查找表可以加快整个过程。
        • 不,我想你误解了.. (0,5) 是指包含两个素数之和的盒子,最后一个数字是 0 和 5。两个素数之和可以以 0 结尾,例如。 11和19
        【解决方案5】:

        你仍然可以接受时间。由于哥德巴赫猜想,每个大于或等于 8 的偶数都可以表示为 2,2 和另外两个素数之和。每个大于或等于 9 的奇数都可以表示为 2,3 和另外两个素数之和。找出质数应该不会花太长时间。

        编辑:实际上,您可以显着加快速度:对于任何偶数 N,找到小于或等于 N-7 的最大素数并选择该素数和 3,然后再寻找两个适合您的总和的素数。对于任何奇数N,找到大于或等于N-6的最大素数并选择它和两个,然后再次选择两个素数。

        【讨论】:

        • 即使没有编辑,这两个素数也可以在您从筛子获得的素数列表的一次双遍中找到,使用两个索引/指针:一个向上,一个向下。这应该几乎是瞬时的,所以筛分阶段的 2 秒可能没问题。
        • 你是对的。我认为他需要在这 3 秒内找到所有不超过 100k 的数字的总和。在这种情况下,加速可能会有所作为。
        【解决方案6】:

        一旦你有一个素数列表和一个具体数字,这不是knapsack problem吗?

        N 是你的容量,素数是你的物品。您对项目数的限制为 4。我会用动态编程来解决这个问题,这应该很快。

        【讨论】:

        • 无意冒犯你,但我看到 StackOverflow 的答案倾向于将问题“减少”为更难的问题。 (因此,即使是对于给定约束可以有效解决的问题,经常出现无用的“这是 NP 完全哈哈”的答案。)在这种情况下,没有必要将其“简化”为背包问题,当(因为哥德巴赫猜想等)几种贪心算法都可以工作。
        • @ShreevatsaR 没有采取任何措施,鉴于我的知识,这是我脑海中浮现的第一件事。没有任何研究,这是我无需寻找任何地方即可实施的解决方案。当然,如果我要解决这个问题,我会先用谷歌搜索,然后再问:-)
        【解决方案7】:

        如果数字大小没有限制(100,000 或更少),则不能保证您的问题有解决方案:请参阅weak Goldbach conjecture

        但很可能这是真的,至少对于计算结果范围内的数字......你确定你的问题不是表达任何数字,最多四个素数的总和?

        由于 2,3,5,7,11,13,17,19,23 提供了很多可能性,我将计算表示为这些数字的 3 和的数字。 (例如 2+3+5=10, 2+3+7=2+5+7=12, 3+5+7=15, 2+3+11=16, 2+5+11=18, 3+ 5+11=19, 2+7+11=20, ... 17+19+23 = 59.)

        然后取你的任意数 N,找到与 N 相差预先计算的 3 个小素数之和之一的最接近的素数。如果您没有找到解决方案,请尝试下一个最接近 N-59 的素数。如果这仍然不起作用,请开始添加其他小素数。

        使用有关prime gaps 的知识来限制您的解决方案...对于低于 155921(大于 100,000)的素数,最大的素数差距是 86。


        附言对于 N = 100,000,您的 Eratosthenes 筛不应该花费 2 秒。您只需要检查除数,直到 100,000 = 316 的平方根。

        【讨论】:

        • 如果没有数字大小的限制,那么3s的时间限制是不合理的;-)
        【解决方案8】:

        这是我的cmets中question referenced给出的推荐:

        • 使用以下方法计算所有小于 N 的素数 埃拉托色尼筛。
        • 制表 两个素数之和的列表。
        • 排序 列表。
        • 检查是否有两个数字 在总和为 N 的列表中。
        • 如果是,打印出对应的四个 素数。

        【讨论】:

          【解决方案9】:

          任何数字也包括小数,因此您也不能将它们表示为质数。还有其他像 9 这样的数字是你做不到的。没有 1 作为素数,你无法很好地控制它。

          我希望问题没有解决方案。

          【讨论】:

          • 9=2+2+2+3,四个素数,耶 =)
          • 哦,拜托,OP 可能只是忽略了陈述“整数”而不是“数字”或最初陈述的问题的另一个微妙细节。
          • 从技术上讲,“任意数字”还包括无理数和非实数复数,但很明显 OP 只对整数感兴趣...
          • 还有汉密尔顿主义者。不要忘记凯莱数——它们总是很有趣,因为非结合乘法几乎可以保证你会不断地犯简单的错误......
          • 好吧,小数部分本来应该是个玩笑,但我已经被 2+2+2+3 的东西迷住了。我以为他的意思是独一无二的。
          猜你喜欢
          • 1970-01-01
          • 2017-12-08
          • 1970-01-01
          • 2011-06-18
          • 1970-01-01
          • 1970-01-01
          • 2021-05-27
          • 1970-01-01
          • 2011-07-05
          相关资源
          最近更新 更多