【问题标题】:How to solve this?如何解决这个问题?
【发布时间】:2022-01-21 14:29:07
【问题描述】:

我正在做一个任务,命令我计算给定的 q 个元素中未来数字的数量。未来数是一个数,它的所有除数(不包括 1 和它自己)都是素数。它根本不打印任何东西。任何人都有一个建议,为什么它不起作用?!!

示例输入: 9

9 7 10 6 17 4 19 21 13

示例输出: 5

我的代码:

#include<bits/stdc++.h>
using std::cin;
using std::cout;
int i,j,count1=0,count_divisors;
int a[10005];
int prime_numbers(int n) {
    if(n<2) return 0;
    if(n==2||n==3) return 1;
    for(i=2;i<=sqrt(n);i++) 
        if(n%i==0) return 0;
    return 1;
}
int main() {
    int q;
    cin>>q;
    for(i=0;i<q;i++) cin>>a[i];
    for(i=0;i<q;i++) {
        count_divisors=0;
        for(j=2;j<=sqrt(a[i]);j++) {
            if(!(a[i]%j)) {
                int the=a[i]/j;
                if(prime_numbers(j)==0) break;
                if(prime_numbers(the)==0) break;
                count_divisors+=2;
            }
            if(j>sqrt(a[i])&&count_divisors!=0) count1++;
        }
    }
    cout<<count1;   
}

【问题讨论】:

标签: c++ variable-assignment


【解决方案1】:

我认为任务是找到一个数组中的所有数字

  • a) 至少有一个除数(1 或数字本身除外)并且
  • b) 所有这些除数都是素数。

一些例子:

  • 9 => 除数:[3]; 3 是质数,所以“9”算数。
  • 7 => 除数:[无];所以“7”不算数。
  • 20 => devisor(s): [2, 5, 10]; 10 是除数,但不是质数,所以“20”不算数。

在给定的示例中,有 5 个数字满足条件,即 9、10、6、4 和 21。这是 5 个数字,因此答案是“5”。

这是为您执行此操作的代码:

#include <iostream>

bool is_prime_numbers(int n) {

    if (n < 2) {
        return false;
    }

    for (int i = 2; i < n; i++)
    {
        if (n % i == 0) {
            return false;
        }
    }
        
    return true;
}

int main() {

    int cnt = 0;
    int arr[1000];

    int q;
    cin >> q;
    //q = 9;

    for (int i = 0; i < q; i++)
        cin >> arr[i];
    /*arr[0] = 9;
    arr[1] = 7;
    arr[2] = 10;
    arr[3] = 6;
    arr[4] = 17;
    arr[5] = 4;
    arr[6] = 19;
    arr[7] = 21;
    arr[8] = 20;*/

    for (int i = 0; i < q; i++) {

        bool found_divisor_prime = false;
        bool found_divisor_other = false;
        
        int val = arr[i];
        for (int div = 2; div < val; div++) {

            //can "val" be devided by "div"?
            if ((val % div) == 0) {
                
                //is "div" a prime number?
                if (is_prime_numbers(div))
                    found_divisor_prime = true;
                else
                    found_divisor_other = true;
            }
        }

        /* we only want to count numbers that:
        * a) have a divisor other than 1 or the number itself
        * b) all divisors are prime numbers
        */
        if (found_divisor_prime && !found_divisor_other)
            cnt++;
    }

    std::cout << cnt;

    return 0;
}

【讨论】:

  • 非常感谢!感谢您的帮助!
【解决方案2】:

第一个错误是 AudioDroid 指出全局变量 i 正在被两个函数更改,即 main 和 prime_numbers。这可以通过定义一个局部变量来解决,比如 k 并循环使用它:

int k;
for (k = 2; k <= sqrt(n); k++) {
    if (n % k == 0) return 0;
}

第二个bug是你写的

if (j > sqrt(a[i]) && count_divisors != 0) count1++;

在内部 for 循环体中,因此它永远不会被执行。它必须从那里取出。修复这两个错误,你会得到你的 5 作为答案。

但是,如果您的 q 大到 10^6 或更大,您的这个解决方案将不是很有效,因为对于每个除数 d,您都在循环 [2; sqrt(d)] 而不是在查找对象中生成直到给定限制的所有素数,并在 O(1) 时间内检查特定除数是否为素数。

#include <memory>
#include <bitset>
#include <iostream>
#include <vector>

int const lim = 1000000;

bool is_future(int const num, std::unique_ptr<std::bitset<lim + 1>> const& is_prime) {
    int div, cnt = 0;
    for (div = 2; div * div <= num; ++div) {
        if (num % div == 0) {
            ++cnt;
            if (!is_prime->test(div)) {
                return false;
            }
            if (div * div != num) {
                ++cnt;
                if (!is_prime->test(num / div)) {
                    return false;
                }
            }
        }
    }
    return cnt != 0;
}

int main() {
    auto sieve = std::make_unique<std::bitset<lim + 1>>();
    sieve->set();
    sieve->set(0, false);
    sieve->set(1, false);
    int i, j;
    for (i = 2; i * i <= lim; ++i) {
        if (sieve->test(i)) {
            for (j = i * i; j <= lim; j += i) {
                sieve->set(j, false);
            }
        }
    }
    int q;
    std::cin >> q;
    std::vector<int> seq;
    seq.reserve(q);
    int k, num;
    for (k = 0; k != q; ++k) {
        std::cin >> num;
        seq.emplace_back(num);
    }
    int ans = 0;
    for (auto const& elem : seq) {
        if (is_future(elem, sieve)) {
            ++ans;
        }
    }
    std::cout << ans << '\n';
    return 0;
}

根据输入中数字的最大值将 lim 常量设置为足够高。通过 unique_ptr 筛子在堆上分配位集。在 lim 的筛子上运行 Eratosthenes 算法的筛子。阅读 q。定义向量序列。使用 reserve 为 q 个元素分配内存,从而避免预分配。增长序列。用 0 初始化计数器 ans。遍历 seq 并为每个元素 elem 调用 is_future 和 elem 和 sieve。 is_future 使用 is_prime (sieve) 作为查找对象来检查除数 div 或 (num/div) 在 O(1) 时间内是否为素数。计数器 cnt 统计 num 的除数在范围 [2; sqrt(数字)]。请注意,我不使用 std::sqrt 因为它很昂贵,如果被调用数千次。如果 cnt 为 0,则该数字在指定范围内没有除数,因此它不是未来,否则它是。如果 is_future 返回 true 增量 ans。输出答案。

@Trương Quang Vinh

seq.reserve(q);

假设你有一个空的向量序列。它的初始容量为 0,这意味着它为 0 个整数分配了一块内存。如果你开始增长这个向量,假设你想读取 10^6 个整数并将它们存储在其中,它必须执行以下操作:

分配更大的内存块

将旧块中的整数复制到新块中

调用位于旧块上的整数的析构函数

释放旧块上的整数使用的内存并将其返回给系统

我用 10^6 个整数做了一个测试,在微软的实现下,vector seq 的容量是这样变化的:

0 -> 1 -> 2 -> 3 -> 4 -> 6 -> 9 -> 13 -> 19 -> 28 -> 42 -> 63 -> 94 -> 141 -> 211 -> 316 - > 474 -> 711 -> 1066 -> 1599 -> 2398 -> 3597 -> 5395 -> 8092 -> 12138 -> 18207 -> 27310 -> 40965 -> 61447 -> 92170 -> 138255 -> 207382 -> 3110 -> 466609 -> 699913 -> 1049869

在向量 seq 改变其容量的每个步骤中,上述 4 个操作都会发生,这当然需要一些时间。在您的情况下,您知道要在向量中存储 q 个整数,并且使用成员函数 reserve,您可以直接分配一个足够大的内存块来保存它们,从而避免上述 4 个操作。

seq.emplace_back(num);

成员函数 emplace_back 直接根据给定的参数构造一个元素,而不是像 push_back 那样从根据给定参数创建的副本构造一个元素,因此更有效。

is_prime->test(num / div)

is_prime 是一个 unique_ptr,它指向位于堆上的 std::bitset 类型的对象。 bitset 类模仿数字的位。在您的情况下,您将在此对象上运行 Eratosthenes 算法的筛子。假设您要生成范围 [2; 中的所有素数; 100]。您的 bitset 对象最初看起来像这样:

111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100

表示数字 0 和 1 的最后两位设置为 0,因为 0 和 1 不是素数。当算法完成时,您的 bitset 对象将如下所示:

00010000000100000100010000010100010000010100000100000100010100010000010100000100010100010100010101100

通过带有 operator-> 的指针 is_prime 调用的函数 test 是 (*is_prime) 的缩写形式。test 将测试位假设 x 是 1 还是 0。如果是 1,则该位表示的数字x 是素数,否则不是。

std::unique_ptr<std::bitset<lim + 1>> const& is_prime

这是作为对 const 的引用传递给函数 is_future 的对象 sieve,这意味着对 is_prime 执行的操作是在 sieve 上完成的,并且承诺不会更改它,因为 is_prime 是对 const 的引用。如果我不在这一行写 auto

auto sieve = std::make_unique<std::bitset<lim + 1>>(); 

我必须写

std::unique_ptr<std::bitset<lim + 1>> sieve = std::make_unique<std::bitset<lim + 1>>(); 

这是 main 中缺少的类型 unique_ptr 的来源。

【讨论】:

  • 非常感谢!但是我还没有看懂你用的函数,因为我最近才学c++,能不能具体解释一下?
  • @Trương Quang Vinh 您需要解释哪些功能?
  • 感谢您的好态度,我对这些功能不太清楚:std::unique_ptr<:bitset>> const& is_prime is_prime->test(num / div) seq .reserve(q); seq.emplace_back(num);
  • @Trương Quang Vinh 我编辑了我的答案并添加了一些解释。
猜你喜欢
  • 2014-10-07
  • 2019-04-20
  • 2011-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多