将任何数字表示为四个素数的总和

时间:2010-04-30 11:29:48

标签: c++ algorithm math

我提出的问题是将任何数字表示为四个素数的总和。

条件:

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

我做了什么:

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

  2. 查找了一个名为Goldbach猜想的概念,该概念将偶数数表示为两个素数的总和。

  3. 然而,我被困在那之外。 任何人都可以帮我解决一下你可能采取的方法吗?

    Eratosthenes的筛子花了两秒钟来计算质量高达100,000的数量。

9 个答案:

答案 0 :(得分:16)

随着时间的推移你仍然可以。由于Goldbach猜想,每个偶数大于或等于8可以表示为2,2和另外两个素数的总和。每个大于或等于9的奇数可以表示为2,3和另外两个素数之和。找出素数不应该花太长时间。

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

答案 1 :(得分:4)

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

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

  1. 使用Eratosthenes的Sieve计算所有小于N的素数。
  2. 制表两个素数之和的列表。
  3. 根据最后一位数字分组到10个std :: set框
  4. 查看号码的最后一位,找到 可以实现这一目标的组合(包括携带)。使用这些来限制搜索范围
  5. 例如,

    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

答案 2 :(得分:2)

如果没有数量限制(100,000或更少),那么您的问题无法保证有解决方案:请参阅weak Goldbach conjecture

然而,最有可能的是,至少对于计算结果范围内的数字而言......你确定你的问题不是表达任何数字 most 四个素数的总和吗? / p>

由于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。


P.S。你的Eratosthenes筛子不应该花2秒钟N = 100,000。您只需要检查最大为100,000 = 316的平方根的除数。

答案 3 :(得分:1)

一旦你有一个素数列表,那么具体的数字不是knapsack problem吗?

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

答案 4 :(得分:0)

以下是我在评论中question referenced给出的推荐:

  
      
  • 计算所有小于N的素数   Eratosthenes的筛子。
  •   
  • 制表表格   两个素数之和的列表。
  •   
  • 排序   列表。
  •   
  • 检查是否有两个号码   在总和为N的列表中。
  •   
  • 如果是,请打印出相应的四个   素数。
  •   

答案 5 :(得分:0)

这是一个 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) . ')');
    }
}

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

答案 6 :(得分:0)

关于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)。

答案 7 :(得分:0)

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

我使用了以下代码:

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.

答案 8 :(得分:-3)

任何数字也包括小数,所以你也不能将它们表示为素数。还有其他数字,比如你不能做的9。没有1作为素数,你无法控制它。

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