C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で素因数のべき乗のGCDが1となる範囲内の数を数える方法

正の整数の範囲を表す2つの数 startend が与えられます。この問題の目標は、範囲 [start, end] に含まれる数のうち、素因数分解した際のすべての素因数の指数(べき乗)の最大公約数(GCD)が 1 となる数の個数を求めることです。

ある数が 2p × 3q × 5r … のように素因数分解できる場合、指数 p, q, r … のGCDが 1 でなければなりません。

例で理解しよう

入力例:

start = 1, end = 10

出力: 素因数のべき乗のGCDが1となる範囲内の数の個数: 6

説明: 該当する数は次のとおりです。

2(21)、3(31)、5(51)、7(71)、8(23)、10(21 × 51)。それぞれの素因数分解におけるべき乗のGCDはすべて 1 です。

入力例:

start = 11, end = 20

出力: 素因数のべき乗のGCDが1となる範囲内の数の個数: 9

説明: 該当する数は次のとおりです。

11(111)、12(31 × 22)、13(131)、14(21 × 71)、15(31 × 51)、17(171)、18(21 × 32)、19(191)、20(22 × 51)。それぞれの素因数分解におけるべき乗のGCDはすべて 1 です。

プログラムで使用するアプローチ

このアプローチでは、範囲 start ~ end にある「完全累乗数ではない」数をすべて数えます。完全累乗数でない数は、上記の条件(べき乗のGCDが1)を必ず満たすためです。そこで、まず範囲内の完全累乗数をすべて見つけ、総数からそれらを差し引きます。

答え = (end − start + 1) − (範囲 [start, end] 内の完全累乗数の個数)

  • 範囲を表す変数 start と end を入力として受け取ります。
  • 3以上の奇数べき乗を格納するためのベクター vec を用意します。
  • 完全平方数を格納するセット sett を用意します。
  • 完全平方数ではない数を格納するセット sett_2 を用意します。
  • 関数 calculate() は、ベクター vec およびセット sett、sett_2 を構築し、完全平方数・非完全平方数・3以上の奇数べき乗の数を分類します。
  • forループで i = 2 から i < size まで走査します。
  • 完全平方数 i*i を sett に挿入します。
  • sett.find(i) != sett.end() が true を返す場合、i は完全平方数であり sett に既に存在するため、何もしません。
  • 現在の数のべき乗が large 未満である限り、whileループを繰り返します。
  • 偶数べき乗は完全平方数として sett に含まれるため、奇数べき乗のみを sett_2 に挿入します。
  • 最後に、forループを使って sett_2 のソート済みの値をベクター vec に挿入します。
  • 関数 GCD_1(long int start, long int end) は範囲を受け取り、素因数のべき乗のGCDが1となる範囲内の数の個数を返します。
  • calculate() を呼び出します。
  • 範囲内の完全平方数の個数を per_sq = floor(sqrtl(end)) − floor(sqrtl(start − 1)) として計算します。
  • upper_bound(vec.begin(), vec.end(), end) − vec.begin() により、vec 内で end を超える最初の位置(top)を求めます。
  • 同様に lower_bound(vec.begin(), vec.end(), start) − vec.begin() により、vec 内で start 以上の最初の位置(bottom)を求めます。
  • 完全累乗数の個数を per_pow = per_sq + (top − bottom) として計算します。
  • 答えは count = (end − start + 1) − per_pow となります。
  • 最後に count を結果として返します。

サンプルコード

#include <bits/stdc++.h>
using namespace std;
#define size 1000005
#define large 1e18

vector < long int > vec;
set < long int > sett;
set < long int > sett_2;

void calculate() {
    for (long int i = 2; i < size; i++) {
        sett.insert(i * i);
        if (sett.find(i) != sett.end()) {
            continue;
        }
        long int total = i;
        while (i * i <= large / total) {
            total *= (i * i);
            sett_2.insert(total);
        }
    }
    for (auto it: sett_2) {
        vec.push_back(it);
    }
}

long int GCD_1(long int start, long int end) {
    calculate();

    long int per_sq = floor(sqrtl(end)) - floor(sqrtl(start - 1));
    long int top = upper_bound(vec.begin(), vec.end(), end) - vec.begin();
    long int bottom = lower_bound(vec.begin(), vec.end(), start) - vec.begin();
    long int per_pow = per_sq + (top - bottom);
    long int count = (end - start + 1) - per_pow;
    return count;
}
int main() {
    long int start = 10, end = 40;
    cout << "素因数のべき乗のGCDが1となる範囲内の数の個数: " << GCD_1(start, end);
    return 0;
}

上記のコードを実行すると、次の出力が得られます。

出力

素因数のべき乗のGCDが1となる範囲内の数の個数: 7
  1. 【C++入門】数値の素因数分解:すべての素因数とその指数(べき乗)を効率的に出力する方法

    この記事では、「整数 N が与えられたとき、その数を割り切るすべての異なる素因数と、それぞれの指数(べき乗)を求めて出力する」という問題を、C++ を使って効率的に解く方法を解説します。 問題の概要 まず、具体例を見てみましょう。 入力:55 出力: 5 の指数:1 11 の指数:1 解説: 55 は 5 × 11 と素因数分解できます。どちらの素因数も 1 回しか現れないため、それぞれの指数は 1 となります。 解法のアプローチ この問題に対する基本的なアプローチは以下の通りです。 N の素因数を見つける。 各素因数が N を何回割り切れるか(=指数)を数える。 結果を出力する。

  2. C++で1からNまでの準素数(Almost Prime)の個数を求める方法

    ある数 N が与えられたとき、1からNまでの範囲に含まれる「準素数(almost prime)」の個数を求める問題を考えてみましょう。準素数とは、異なる素因数をちょうど2つ持つ数のことです。素因数以外の約数(合成数の約数)はいくつあっても構いませんが、その中に含まれる素因数は正確に2種類である必要があります。例えば、Nが10の場合、出力は2になります。これは、条件を満たす数が 6(= 2 × 3)と 10(= 2 × 5)の2つしか存在しないためです。アプローチ:エラトステネスの篩を活用するこの問題を効率的に解くには、エラトステネスの篩(Sieve of Eratosthenes)を使って素数