首页 > 其他分享 >质数与合数

质数与合数

时间:2023-12-03 22:33:50浏览次数:21  
标签:prime int 质数 pri sqrt 合数

质数与合数

判断质数

显然,每个合数都会有相对较小的质因子。

若 \(a\) 为合数,则 \(a = p\cdot q(p,q>1)\)。易证 \(p、q\) 中一定有一个不超过 \(\sqrt a\)(若两个都超过 \(\sqrt a\),则 \(p\cdot q > a\))。

更严格地,若 \(a\) 为合数,则一定存在质数 \(p|a\),且 \(p \leq \sqrt a\)。

bool is_prime(int x)
{
	for (int i = 2; i * i <= x; i++)
    	if (x % i == 0) return false;
    return true;
}

埃拉托斯特尼筛法

考虑这样一件事情:对于任意一个大于 \(1\) 的正整数 \(n\) ,那么它的 \(x\) 倍就是合数\(x > 1\)。利用这个结论,我们可以避免很多次不必要的检测。

如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。

vector<int> prime;
bool no_prime[N];

void Eratosthenes(int n)
{
    no_prime[0] = no_prime[1] = true;
    for (int i = 2; i <= n; i++)
    {
		if (!no_prime[i])
        {
			for (int j = i << 1; j <= n; j += i)
            {
                no_prime[i] = true;
            }
        }
    }
}

线性筛法

埃氏筛法仍有优化空间,它会将一个合数重复多次标记。有没有什么办法省掉无意义的步骤呢?答案是肯定的。

如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 \(O(n)\) 了。

vector<int> pri;
bool not_prime[N];

void pre(int n)
{
	for (int i = 2; i <= n; ++i)
	{
		if (!not_prime[i])
		{
			pri.push_back(i);
		}
		for (int pri_j : pri)
		{
			if (i * pri_j > n)
				break;
			not_prime[i * pri_j] = true;
			if (i % pri_j == 0)
			{
				// i % pri_j == 0
				// 换言之,i 之前被 pri_j 筛过了
				// 由于 pri 里面质数是从小到大的,所以 i 乘上其他的质数的结果一定会被pri_j 的倍数筛掉,就不需要在这里先筛一次,所以这里直接 break掉就好了
				break;
			}
		}
	}
}

例题

P1835 素数密度

首先结合上面推论,理论上只要利用不超过 \(\sqrt n\) 的质数就可以筛除 \(n\) 范围内的所有合数。

对于题目区间 \(2147483647\),先预先求出 \(\sqrt {2147483647} = 50000\) 以内所有质数,再用这些质数对 \([L,R]\) 上的合数筛除即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

const int N = 1e6 + 10;

using ll = long long;

bool is_prime[N];
bool no_prime[N];

int pri[10000];

int init()
{
	int cnt = 0;
	is_prime[0] = is_prime[1] = false;
	for (int i = 2; i <= 50000; ++i)
		is_prime[i] = true;
	for (int i = 2; i * i <= 50000; i++)
	{
		if (is_prime[i])
		{
			for (int j = i * i; j <= 50000; j += i)
			{
				is_prime[j] = false;
			}
		}
	}
	for (int i = 2; i <= 50000; i++)
	{
		if (is_prime[i])
			pri[cnt++] = i;
	}
	return cnt;
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);
	int cnt = init();
	ll L, R;
	std::cin >> L >> R;
	for (int i = 0; i < cnt; i++)
	{
		for (ll j = std::max(2ll, (L - 1) / pri[i] + 1) * pri[i]; j <= R; j += pri[i])
		{
			no_prime[j - L] = true;
		}
	}
	int ans = 0;
	no_prime[1] = true;
	for (ll i = L; i <= R; i++)
	{
		if (!no_prime[i - L])
			ans++;
	}
	std::cout << ans;
	return 0;
}

标签:prime,int,质数,pri,sqrt,合数
From: https://www.cnblogs.com/kdlyh/p/17873946.html

相关文章

  • 第 132 场周赛——质数小结论,并查集配Floyd
    https://www.acwing.com/activity/content/competition/problem_list/3648/B题收获:1.利用题目告诉的结论:1e9范围质数之差小于3002.一个数不被2-a的任何数整除等价于他的最小质因子需要大于ac题:初步宏观思路:不难想到用并查集维护类别,只需将每一个类缩成一个点,由于最多只有500......
  • 2023-12-02:用go语言,如何求模立方根? x^3=a mod p, p是大于等于3的大质数, a是1到p-1范围
    2023-12-02:用go语言,如何求模立方根?x^3=amodp,p是大于等于3的大质数,a是1到p-1范围的整数常数,x也是1到p-1范围的整数,求x。p过大,x不能从1到p-1遍历。答案2023-12-02:灵捷3.5大体步骤如下:1.判断是否存在模立方根。有0,1,3个根这三种情况。1.1.求p-1和3的最大公约数gcd(p-1,3)......
  • 预处理组合数
    预处理组合数基本做法针对大多数仅仅是利用组合数求解问题的题目运用递推法打表,不仅方便,而且可以稳稳地控制复杂度,对于需要多次引用组合数的题目效果极佳:基于组合数公理性质:\[C^m_n=C^{n-m}_n\]推得:\[C^m_n=C^{m-1}_{n-1}+C^m_{n-1}\]由这个递推公式就可以熟练的写出组合......
  • js和python获取1-100之间的质数
    jsfor(leti=2;i<=100;i++){letiszs=truefor(letj=2;j<i;j++){if(i%j===0){iszs=falsebreak}}if(iszs){zs.push(i)}}console.log(zs)pythonzs=[]foriinrange(2,101):iszs......
  • 郑轻工 3097. 筛质数 + 二分 = 小模拟
    importjava.util.Arrays;importjava.util.Scanner;classMain{staticint[]pri=newint[100];staticintidx;publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intx=sc.nextInt();init......
  • 使用yield返回集合数据
    staticvoidMain(string[]args){foreach(vararginGetStrings()){Console.WriteLine(arg);}Console.ReadLine();}staticIEnumerable<string>GetStrings(){yieldreturn"1";Console.WriteLine("1返回去了......
  • 组合数学(苹果盘子问题)
    初赛题目中往往会出现将多少东西(相同或者不同),分到一些容器(相同或者不同)中,允许或者不允许空的问题,这里我们就统一总结一下。本篇博客中,物品统一称为苹果,容器统一称为盘子,因而得名为苹果盘子问题。1.苹果相同,盘子不同,不允许空思路:既然苹果是相同的,盘子是不同的,那么实际上我们的问......
  • 循环嵌套 质数
    7-1循环嵌套计算s=1+(1+2)+(1+2+3)+……+(1+2+……+n)。输入格式:输入在一行中给出n的值。输出格式:在输出行显示计算出的结果。输入样例:在这里给出一组输入。例如:20输出样例:在这里给出相应的输出。例如:sum=1540解题思路:1.观察需要计算的式子可知,需要计算n次......
  • 组合数模板(省赛)
    组合数+快速幂#include<bits/stdc++.h>//#pragmaGCCoptimize("Ofast")#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>#include<cmath>//#definedoublelongdou......
  • 组合数学
    排列组合\[A_m^n=\frac{n!}{(n-m)!}\]\[C_{m}^{n}=\frac{n!}{m!(n-m)!}\]\[C^n_0+C_1^n+C_2^n+...+C_n^n=2^n\]\[C_m^n+C_m^{n+1}=C_{m+1}^{n+1}\]\[C_m^n=C^n_{n-m}\\\\\\\\\\\\\\\\C^n_0=1\]基本计数原理加法原理:做一件事,完成它可以有\(n\......