如果我有一个数字不能很好地分解(给定一个可接受的素数列表),我该如何找到下一个最接近的数字?例如,给定数字5917,与素数列表3,5,7的最接近的数字是多少?在这个例子中是6000.
我有一些会暴力的东西找到答案,但必须有一个更优雅的解决方案.
- const UInt32 num = 5917;
- const CVector<UInt32> primes = { 2,3,7 };
- const size_t size = primes.size();
- UInt32 x = num;
- while (x < num * 2)
- {
- const UInt32 y = x;
- for(size_t i = 0; i < size && x > 1; ++i)
- {
- while(x % primes[i] == 0)
- {
- x /= primes[i];
- }
- }
- if (x == 1)
- {
- cout << "Found " << y << endl;
- break;
- }
- else
- {
- x = y + 1;
- }
- }
编辑
我创建了一个使用暴力法和3种方法作为答案的测试,并得到了一些令人惊讶的结果.所有4个版本都会产生正确的答案(所以感谢你的贡献),然而,强力方法似乎是最快的一个数量级.我尝试了几个不同的系统,编译器和架构,这些系统都是大致一致的结果.
测试代码可以在这里找到:http://ideone.com/HAgDsF.请随时提出建议.
解决方法
- vector<int> primes = { 2,7 };
- int num = 5917;
- // initialize bestCandidate as a power of some prime greater than num
- int bestCandidate = 1;
- while (bestCandidate < num) bestCandidate *= primes[0];
- set<int> s;
- s.insert(1);
- while (s.size()) {
- int current = *s.begin();
- s.erase(s.begin());
- for (auto p : primes) { // generate new candidates
- int newCandidate = current * p;
- if (newCandidate < num) {
- // new lower candidates should be stored.
- if (s.find(newCandidate) == s.end())
- s.insert(newCandidate);
- }
- else {
- if (newCandidate < bestCandidate) bestCandidate = newCandidate;
- break; // further iterations will generate only larger numbers
- }
- }
- }
- cout << bestCandidate;
接下来我想对提出的解决方案进行分析.让我用np作为一些素数; n作为一个数字找到最接近的结果; minP作为列表中的最小素数.
>我的解决方案生成低于n的所有可能的值.旧值生成新值.每个值只能使用一次作为生成源.如果新值超过n,则被视为有效的候选者.如果列表将包含低于n的所有素数,则algo可以执行得很好.我不知道算法的漂亮的时间复杂度公式,但是低于n的有效候选人数乘以先前因子的对数.日志来自设置的数据结构操作.如果n可以小到足以分配大小为n的数组来标识已经生成的值,那么我们可以摆脱Log因子,一个简单的列表可以保存生成源值而不是set.
>您的初始解决方案有O(n(np logminP(n))).您检查每个数字是有效的,然后逐个从n到2n支付每个支票的np logminP(n).
> Recursive solution by @anatolyg在“访问”一些有效数字很多次,这是非常低效的有很大的缺陷.可以通过引入指示该号码已被“访问”的标志来修复.例如12 = 2 * 2 * 3将从6 = 2 * 3和4 = 2 * 2进行访问.次要缺陷是无数上下文切换,并支持每个呼叫的状态.该解决方案有一个全局变量,它将整个全局命名空间,这可以通过添加一个函数参数来解决.
> Solution by @dasblinkenlight缺乏效率,因为已经“使用”的候选人被用于生成已经存在于该集合中的数字的新候选者.虽然我已经借用了这个想法.
基于@גלעד ברקן’s answer,我创建了一个c解决方案,这似乎是渐近更有效率,因为没有对数因子.然而,我拒绝使用双对数和左整数解.这个想法很简单我们有一个低于num的产品列表.每个产品都是从第一个primesUsed素数生成的.然后,我们尝试使用下一个素材生成新产品.这种方法保证产生独特的产品:
- vector<int> primes = { 2,7,11,17,23 };
- int num = 100005917;
- int bestCandidate = INT_MAX;
- list<pair<int,int> > ls;
- ls.push_back(make_pair(1,0));
- while (ls.size()) {
- long long currentProd = ls.front().first;
- int primesUsed = ls.front().second;
- ls.pop_front();
- int currentPrime = primes[primesUsed];
- while (currentProd < num) {
- if(primesUsed < primes.size() - 1)
- ls.push_back(make_pair(currentProd,primesUsed + 1));
- currentProd *= currentPrime;
- }
- bestCandidate = min((long long)bestCandidate,currentProd);
- }
- cout << bestCandidate;