【问题标题】:optimizing the code for sieve优化筛子的代码
【发布时间】:2012-10-21 08:45:21
【问题描述】:

这是我编写的用于在上限可以大到 1000000000 的范围之间查找素数的代码。我使用了 Hashmap,除了 2 之外没有存储任何偶数,也没有存储 sero 和 1 .但是当我使用输入 lb = 1 和 ub =1000000000 运行此代码时,它仍然会出现运行时错误,(内存不足)。请帮助

这是我的代码:-

import java.util.HashMap;
import java.util.Iterator;

import java.util.Scanner;

class Samp {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int t, limit, m, n;
        double lb, ub, c;

        t = sc.nextInt();
        while (t > 0) {
            c = 3;
        HashMap<Integer, Boolean> primeflags = new HashMap<Integer, Boolean>();
            primeflags.put(2, true);
            lb = sc.nextDouble();
            ub = sc.nextDouble();
            while (c <= ub) {
                primeflags.put((int) c, true);
                c = c + 2;
            }
            limit = (int) Math.sqrt(ub);

            for (m = 2; m <= limit; m++) {

                for (n = m * m; n <= ub; n += m) {

                    if (primeflags.containsKey(n))
                        primeflags.remove(n);
                }

            }
            Iterator<Integer> iterator = primeflags.keySet().iterator();
            while (iterator.hasNext()) {  
                   Integer key = (Integer) iterator.next(); 

                   if(key >= lb)
                   System.out.println(key); 
                }



            --t;

        }
       sc.close();
     }    
}

好的,收到答案后,我编码了这个:但它仍然在给 TLE

import java.util.BitSet;
import java.util.Scanner;
public class Prime {
    private static BitSet bitSet = new BitSet(1000);
    private static int max = 3;

      public static boolean isPrime(int n) {
          if(n == 2)
                      return true;
          if(n < 3 || n % 2 == 0)
               return false;
          if(n <= max)
              return !bitSet.get(n / 2);
          for(int i = 3; i <= n; i += 2) {
              if(i * 2 > n)
                 break;
              if(!bitSet.get(i / 2)) {
                  int multiple = max / i;
                  multiple *= i;
                  if(multiple <= i)
                multiple = i * 2;
                  clearMultiple(i, multiple, n);  
              }
          }
          max = n;
          return !bitSet.get(n / 2);
      }

    private static void clearMultiple(int prime, int multiple, int max) {
        while(multiple <= max) {
                    setNotPrime(multiple);
                    multiple += prime;
                   }

    }

    private static  void setNotPrime(int n) {

        if(n % 2 == 0)
             return;
              bitSet.set(n / 2, true);
}     


    public static void getPrimeGreaterOrEqual(int n,int upperbound) {
            // make sure we start with an odd number
        if( n == 1 || n == 0){
            System.out.println(2);
            n = 3;
        }    
    if(n % 2 == 0 && n != 2)
                    ++n;
                // loop until we found one
                    while(n <= upperbound) {
                //if the number is registered as prime return it
                if(isPrime(n))
                       System.out.println(n);
                       // else check next one
                        n += 2;


               }  
            }

    public static void main(String args[])
    {
        Scanner sc = new Scanner(System.in);
        int t,lb,ub;
         t = sc.nextInt();
        while(t > 0){
            lb = sc.nextInt();
            ub = sc.nextInt();

            getPrimeGreaterOrEqual(lb, ub);

            --t;
       }
       sc.close();
    }
}

【问题讨论】:

  • 可以通过轮分解进一步优化初筛(查看维基百科文章)。

标签: java sieve-of-eratosthenes


【解决方案1】:

将您的算法转换为使用BitSet,您将看到巨大的性能和内存使用改进。

如果您四处搜索,您会发现此算法的许多变体。例如,您可以看看这个:http://www.dreamincode.net/forums/topic/192554-secret-code-vii-prime-numbers/

【讨论】:

  • 你能详细说明一下吗?我的意思是指定一个例子
  • 我正要说。哦,上帝,一个有十亿个条目的哈希图
  • @Wug : 还不到一半
  • @Aleksander :好的,这是我使用 Bitset 修改的代码,我使用给出的链接代码来修改我的代码。但它仍然提供 TLE :
  • @ALeksander:我已经上传了问题中的修改代码。请参阅
【解决方案2】:

我手头没有 Java 的实现。我用 C++ 编写了以下实现,并用它来计算高达 1.000.000.000.000 的素数:

void sieve(std::size_t bound, std::ostream& os)
{
    using uint_t = unsigned int;
    constexpr auto bits = sizeof(uint_t) * CHAR_BIT;
    auto&& index = [](std::size_t i) { return (i - 3) / 2 / bits; };
    auto&& mask = [](std::size_t i) { return uint_t{1} << ((i - 3) / 2 % bits); };
    auto size = index(bound - 1) + 1;
    std::unique_ptr<uint_t[]> crossed{new uint_t[size]};
    std::fill(crossed.get(), crossed.get() + size, uint_t{0});
    for (std::size_t i = 3; i < bound; i += 2) {
        if ((crossed[index(i)] & mask(i)) == 0) {
            auto q = i * i;
            if (q >= bound) {
                break;
            }
            for (; q < bound; q += i) {
                crossed[index(q)] |= mask(q);
            }
        }
    }
    os << "2\n";
    for (std::size_t i = 3; i < bound; i += 2) {
        if ((crossed[index(i)] & mask(i)) == 0) {
            os << i << '\n';
        }
    }
}

【讨论】:

  • 这里实现了一个bitset,将需要的内存减少了8倍,并且只考虑奇数的位进一步减少了一半的内存使用。
  • 它实际上将内存需求减少了 8 倍以上。更像是 100 次(很容易甚至更多)。它是针对Map.EntryIntegerBoolean 的实例的一点点,加上对Map.Entry 的引用,加上HashMap 中的更多开销,加上...
  • @MarkoTopolnik:与 HashMap(OP 的方法)相比,是的。我不清楚我是在与使用 1 字节表示数字是否为素数的天真方法进行比较(我在写评论时有点偏题)。
  • @nhahtdh 嘿嘿,如果他只是实现了你的“幼稚”方法,那么节省的成本已经远远超过了进入 bitset 后的节省 :)
【解决方案3】:

你可以在 find 后面加上质数。所以你不需要更多的内存。 您可以使用此选项运行您的代码。 -Xms512M -Xmx2048M

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-23
    • 2018-02-14
    • 2019-07-03
    • 1970-01-01
    相关资源
    最近更新 更多