3

有一种简单的密码可以将数字转换为 . ( )

为了将数字(0 .. 2147483647)加密为这种表示,我(认为我)需要:

  • 质因数分解
  • 对于给定的pp是 Prime ), p 的顺序序列(即PrimeOrd(2) == 0PrimeOrd(227) == 49

一些例子

    0 . 6 (()())
    1 () 7 (...())
    2 (()) 8 ((.()))
    3 (.()) 9 (.(​​()))
    4 ((())) 10 (().())
    5 (..()) 11 (..())
    227 (.................................................. ())
    2147483648 ((........()))

我的问题源代码


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

static class P
{
    static List<int> _list = new List<int>();

    public static int Nth(int n)
    {
        if (_list.Count == 0 || _list.Count < n)
            Primes().Take(n + 1);

        return _list[n];
    }

    public static int PrimeOrd(int prime)
    {
        if (_list.Count == 0 || _list.Last() < prime)
            Primes().First(p => p >= prime);

        return (_list.Contains(prime)) ? _list.FindIndex(p => p == prime) : -1;
    }

    public static List<int> Factor(int N)
    {
        List<int> ret = new List<int>();
        for (int i = 2; i ≤ N; i++)
            while (N % i == 0)
            {
                N /= i;
                ret.Add(i);
            }

        return ret;
    }

    public static IEnumerable<int> Primes()
    {
        _list = new List<int>();

        _list.Add(2);
        yield return 2;

        Func<int, bool> IsPrime = n => _list.TakeWhile(p => p ≤ (int)Math.Sqrt(n)).FirstOrDefault(p => n % p == 0) == 0;

        for (int i = 3; i < Int32.MaxValue; i += 2)
        {
            if (IsPrime(i))
            {
                _list.Add(i);
                yield return i;
            }
        }
    }

    public static string Convert(int n)
    {
        if (n == 0) return ".";
        if (n == 1) return "()";

        StringBuilder sb = new StringBuilder();
        var p = Factor(n);
        var max = PrimeOrd(p.Last());
        for (int i = 0; i ≤ max; i++)
        {
            var power = p.FindAll((x) => x == Nth(i)).Count;
            sb.Append(Convert(power));
        }
        return "(" + sb.ToString() + ")";
    }
}

class Program
{
    static void Main(string[] args)
    {
        string line = Console.ReadLine();
        try
        {
            int num = int.Parse(line);
            Console.WriteLine("{0}: '{1}'", num, P.Convert(num));
        }
        catch
        {
            Console.WriteLine("You didn't entered number!");
        }
    }
}

问题是程序 PrimeOrd 的缓慢性。你知道一些更快的解决方案来找出素数中的素数顺序吗?

标题

如果您知道如何加快查找素数的顺序,请提出一些建议。:-)

谢谢你。


PS 小于 2,147,483,648 的最大素数是2,147,483,647,它是第105,097,565 个素数。没有必要期望比 2^31 更大的数字。

4

3 回答 3

6

这不是您在运行时应该做的事情。更好的选择是预先计算所有这些素数,然后以某种方式将它们放入您的程序中(静态数组或要读入的文件)。然后,慢速代码作为开发过程的一部分运行(无论如何都很慢:-),而不是在您需要速度的地方。

那么这只是某种查找的问题,而不是每次需要它们时都计算它们。

于 2009-05-30T13:16:19.163 回答
2

您应该将素数缓存到 _list,然后将其用于 Factor 和 PrimeOrd。此外,避免运算符 LINQ 运算符(如 TakeWhile)创建的值会被丢弃。

这是一个优化的版本:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

static class P
{
    private static List<int> _list = new List<int>();

    public static int Nth(int n)
    {
        if (_list.Count == 0 || _list.Count <= n)
        {
            GenerateNextPrimes().First(p => _list.Count >= n);            
        }

        return _list[n];
    }

    public static int PrimeOrd(int prime)
    {
        var primes = GrowPrimesTo(prime);
        return primes.IndexOf(prime);
    }

    public static List<int> Factor(int N)
    {
        List<int> ret = new List<int>();        
        GrowPrimesTo(N);

        for (int ixDivisor = 0; ixDivisor < _list.Count; ixDivisor++)
        {
            int currentDivisor = _list[ixDivisor];

            while (N % currentDivisor == 0)
            {
                N /= currentDivisor;
                ret.Add(currentDivisor);
            }

            if (N <= 1)
            {
                break;
            }
        }

        return ret;
    }

    private static List<int> GrowPrimesTo(int max)
    {
        if (_list.LastOrDefault() >= max)
        {
            return _list;
        }

        GenerateNextPrimes().First(prime => prime >= max);
        return _list;        
    }

    private static IEnumerable<int> GenerateNextPrimes()
    {
        if (_list.Count == 0)
        {
            _list.Add(2);
            yield return 2;
        }

        Func<int, bool> IsPrime =
            n =>
            {
                // cache upperBound
                int upperBound = (int)Math.Sqrt(n);
                for (int ixPrime = 0; ixPrime < _list.Count; ixPrime++)
                {
                    int currentDivisor = _list[ixPrime];
                    if (currentDivisor > upperBound)
                    {
                        return true;
                    }

                    if ((n % currentDivisor) == 0)
                    {
                        return false;
                    }
                }

                return true;
            };

        // Always start on next odd number
        int startNum = _list.Count == 1 ? 3 : _list[_list.Count - 1] + 2;
        for (int i = startNum; i < Int32.MaxValue; i += 2)
        {
            if (IsPrime(i))
            {
                _list.Add(i);
                yield return i;
            }
        }
    }

    public static string Convert(int n)
    {
        if (n == 0) return ".";
        if (n == 1) return "()";

        StringBuilder sb = new StringBuilder();
        var p = Factor(n);
        var max = PrimeOrd(p.Last());
        for (int i = 0; i <= max; i++)
        {
            var power = p.FindAll(x => x == Nth(i)).Count;
            sb.Append(Convert(power));
        }
        return "(" + sb.ToString() + ")";
    }
}

class Program
{
    static void Main(string[] args)
    {        
        string line = Console.ReadLine();
        int num;

        if(int.TryParse(line, out num))
        {   
            Console.WriteLine("{0}: '{1}'", num, P.Convert(num));             
        }
        else
        {
            Console.WriteLine("You didn't entered number!");
        }
    }
}
于 2009-05-30T14:30:55.597 回答