-
【C++】数値が任意の基数(2〜32)で指定した桁数として表現できるか判定する方法
ある整数 n と桁数 d が与えられたとき、n を「2 から 32 までのいずれかの基数」における d 桁の数値として表現できるかどうかを判定する方法を解説します。 例えば、n = 8、d = 4 の場合を考えてみましょう。8 は 2 進法では「1000」と表され、これはちょうど 4 桁です。このように、特定の基数において指定された桁数で表現できるかを確認するのが本記事のテーマです。 アルゴリズムの考え方 基本的なアプローチはシンプルで、「2 から 32 までのすべての基数を一つずつ順番にチェックする」というものです。各基数に対する判定は、以下の手順で行います。 終了条件: 数値が基数よ
-
C++で与えられた数がその桁の階乗の合計を割り切るかどうかを判定する方法
問題の概要ある整数が与えられたとき、その数自身が「各桁の階乗の合計」を割り切るかどうかを判定する方法を解説します。例として、数が19の場合を考えてみましょう。各桁の階乗の合計は次のように計算されます。(1! + 9!) = 1 + 362880 = 362881362881 ÷ 19 = 19099 となり余りは0なので、19は各桁の階乗の合計を割り切ることができる数です。解決のアプローチこの問題は、以下の手順で解くことができます。元の数を一時変数に保存しておく数の各桁を取り出し、それぞれの階乗を計算して合計する合計が元の数で割り切れるかどうかを判定し、結果を返すC++による実装例#inclu
-
C++で指定された数がプロニック数(Pronic Number)かどうかを判定する方法
プロニック数(Pronic Number)とは、点を長方形の形にきれいに配置できる数のことで、「矩形数」と呼ばれることもあります。その定義は非常にシンプルで、2つの連続する整数の積として表される数です。つまり、プロニック数 n は次の式で表せます。n = x × (x + 1)最初のいくつかのプロニック数を列挙すると、0, 2, 6, 12, 20, 30, 42, 56, 72, 90, 110, 132, 156, 182, 210, 240, 272, 306, 342 となります。プロニック数の具体例2 = 1 × 26 = 2 × 312 = 3 × 420 = 4 × 530 =
-
C++で数値を合計が等しい複数のセグメントに分割できるか判定する方法
この記事では、ある数値を合計が等しい複数のセグメントに分割できるかどうかを判定するC++プログラムを紹介します。例えば、74325 という数値は (7)、(4, 3)、(2, 5) の3つの部分に分割でき、それぞれの合計はすべて 7 で等しくなります。この問題を解決するためには、以下の手順に従います。数値を文字列として受け取る接頭辞和(プレフィックスサム)を格納するための配列を用意する2番目の要素から最後の要素まで走査します。このとき最初のセグメントは 0 から i-1 までとなり、その合計は prefix_sum[i - 1] に格納されます別の変数を使って 1 から n まで走査しながら、
-
C++で巨大な数値が13で割り切れるかどうかを判定する方法
本記事では、ある数値が13で割り切れるかどうかを判定する方法を解説します。ここで扱う数値は非常に大きいため、通常の整数型では扱えず、文字列として入力を受け取ります。13で割り切れるための判定条件ある数値が13で割り切れるかどうかは、以下のいずれかの性質を使って確認できます。方法1:3桁ブロックの交互和を利用する数値を右から左へ向かって3桁ずつのブロックに分割し、それらを交互に加算・減算します(交互和)。この結果が13で割り切れる場合、元の数値も13で割り切れます。例として「2911285」を見てみましょう。3桁ごとのブロックに分けると「2」「911」「285」となり、交互和は 2 − 911
-
C++で連結リストが循環リンクリストかどうかを判定する方法
この記事では、連結リスト(リンクリスト)が循環リンクリストであるかどうかを判定する方法を解説します。循環リンクリストの判定アルゴリズム連結リストが循環しているかどうかを確認するには、以下の手順を実行します。先頭ノード(ヘッダーノード)へのポインタを別の変数に保存しておきます。リストを順番に走査していきます。走査中に、あるノードのnextがNULLになった場合はリストの終端に到達したことを意味するため、循環リンクリストではありません。逆に、走査中のノードが最初に保存しておいた先頭ノードと一致した場合は、リストが一周して元の位置に戻ったことを意味するため、循環リンクリストです。C++での実装例#i
-
C++で連結リストがペアごとにソートされているか判定する方法
ペアごとのソート判定とはn個の要素を持つ連結リストLが与えられたとき、そのリストが「ペアごとにソートされているか」を判定します。たとえば、リストが {8, 10, 18, 20, 5, 15} である場合、(8, 10)、(18, 20)、(5, 15) の各ペアがいずれも昇順に並んでいるため、このリストはペアごとにソートされていると判断できます。要素数が奇数の場合は、ペアをなさない最後の1要素が無視されます。アルゴリズムの考え方アプローチは非常にシンプルです。リストを先頭から順に走査し、隣接する2つの要素を1組のペアとして取り出して、それぞれのペアが昇順に並んでいるかを確認します。ソートされ
-
C++で数値が「異常数(アンユージュアル・ナンバー)」かどうかを判定する方法
異常数(アンユージュアル・ナンバー)とは?本記事では、ある数値が異常数(unusual number)であるかどうかを判定する方法を解説します。異常数とは、その数の最大の素因数が、その数自身の平方根よりも厳密に大きい数のことです。異常数の例としては、次のような数が挙げられます。2, 3, 5, 6, 7, 10, 11, 13, 14, 15, 17, 19, 20, 21, 22, 23, 26, 28, 29, 31, 33, 34, 35, 37, 38, 39, 41, 42, 43, 44, 46判定の考え方この問題を解くための手順はシンプルです。まず対象の数の最大の素因数を求め、そ
-
C++で数値がBleak(ブリーク)かどうかを判定する方法
本記事では、ある数値がBleak(ブリーク)であるかどうかを判定する方法を解説します。Bleak数とは、任意の非負整数 x に対して、x と x のセットビット数(2進表現で1となっているビットの個数)との和がその数に一致しない、すなわち x + set_bit_count(x) ≠ n が成り立つような数のことです。 言い換えれば、n = x + セットビット数(x) を満たす非負整数 x がひとつも存在しない場合、n は Bleak 数であるといえます。 判定の考え方 判定のアプローチは非常にシンプルです。1 から n-1 までの各整数 i について「i + i のセットビット数」を計算し
-
C++である数が別の数のすべての素因数で割り切れるかどうかを判定する方法
問題の概要 2つの整数が与えられたとき、一方の数がもう一方の数のすべての素因数で割り切れるかどうかを判定します。 例として、ある数が 120 の場合、その素因数は {2, 3, 5} です。もう一方の数が 75 であれば、その素因数は {3, 5} となります。120 は 3 でも 5 でも割り切れるため、この場合の答えは「Yes」です。 アルゴリズムの考え方 この問題は、最大公約数(GCD)を利用することで効率的に解くことができます。手順は以下の通りです。 もし相手の数が 1 であれば、素因数を一切持たないため、答えは常に true になります。 それ以外の場合は、まず2つの数の GCD
-
C++で行列内に指定した積となるペアが存在するかどうかを判定する方法
本記事では、N × M のサイズの行列と、目標となる積 K が与えられたとき、その積 K になるような2つの要素のペアが行列内に存在するかどうかを判定するアルゴリズムを解説します。問題の概要例として、次のような 4 × 4 の行列を考えてみましょう。12345678910111213141516このとき、K = 42 が与えられた場合、6 × 7 = 42 となるため、ペア (6, 7) が存在することになります。解法のアプローチ:ハッシュを活用この問題はハッシュテーブルを使うことで効率的に解けます。基本的な考え方は以下の通りです。行列の要素を走査しながら、ハッシュセットに要素を登録していきま
-
C++で連結リスト内に積がKになるペアが存在するかを判定する方法
問題の概要 要素の集合と目標となる積 K が与えられたとき、連結リスト内に「積が K と等しくなる2つの数値」が存在するかどうかを判定します。該当するペアが1つだけならそれを出力し、複数存在する場合はいずれか1つを出力すればよいものとします。 たとえば、連結リストが {2, 4, 8, 12, 15} で K = 16 の場合、2 × 8 = 16 となるため、結果として (2, 8) を返します。 アルゴリズム:ハッシュを活用したアプローチ この問題は、ハッシュテーブル(unordered_set)を利用することで効率的に解くことができます。手順は以下のとおりです。 空のハッシュセットを
-
C++で実装するゲーム理論のミニマックス法とアルファ・ベータ枝刈りの徹底解説
概要アルファ・ベータ枝刈り(Alpha-Beta Pruning)は、ミニマックス法で用いられる最適化手法の一つです。このアルゴリズムの基本的な考え方は、すでにより良い手が存在することが確定しているゲーム木の枝を、評価せずに切り捨てる(枝刈りする)ことで、探索の無駄を省き計算を高速化するというものです。このアルゴリズムでは、次の2つの新しいパラメータを導入します。アルファ(α) − 現在のレベル、またはそれより上のレベルにおいて、最大化プレイヤーが保証できる最良の値(最大値)ベータ(β) − 現在のレベル、またはそれより上のレベルにおいて、最小化プレイヤーが保証できる最良の値(最小値)例次のよ
-
C++でN個の数値から作るN/2ペアの平方和を最小化するアルゴリズム
問題概要n個の要素を持つ配列が与えられたとき、その要素を使ってn/2個のペアを作成し、各ペアの合計値を2乗したものの総和(平方和)が最小になるようにするのが課題です。例次の配列が与えられたとします。arr[] = {5, 10, 7, 4}この場合、(4, 10) と (5, 7) のようにペアを作ると、平方和は最小値の 340 になります。計算内容は以下の通りです。(4 + 10)² = 14² = 196(5 + 7)² = 12² = 144196 + 144 = 340アルゴリズム最小の平方和を求めるための手順は以下の通りです。配列を昇順にソートする配列の先頭と末尾を指す2つの変数(s
-
C++で実装する!隣接する生徒の点数に基づくテディ配布数の最小化アルゴリズム
問題概要N人の生徒と、それぞれの生徒が取得した点数を表す配列が与えられます。学校はこれらの生徒にテディベアを賞品として配布することを決めました。しかし、学校はコストを抑えたいと考えているため、以下の制約条件を満たしながら、配布するテディの総数を最小化することを目標とします。すべての生徒は、少なくとも1つのテディを受け取る必要があります隣り合って座っている2人の生徒のうち、点数が高い方の生徒は、低い方の生徒よりも多くのテディを受け取る必要があります同じ点数を持つ2人の生徒は、異なる数のテディを受け取っても構いません具体例例として、生徒が3人おり、その点数が次の配列で表されている場合を考えます。a
-
C++で配列の全要素にXOR演算を適用して合計を最小化する方法
問題の説明サイズNの配列が与えられます。配列の各要素とある整数XとのXOR演算を行ったとき、その結果の合計が最小となるようなXを見つけてください。例として、入力配列が arr[] = {8, 5, 7, 6, 9} の場合、最小合計は 30 になります。各配列要素の2進数表現は次のとおりです。8 : 1000 5 : 0101 7 : 0111 6 : 0110 9 : 1001X = 5 のとき、XOR演算後の各値と合計は以下のようになります。8 ^ 5 = 13 5 ^ 5 = 0 7 ^ 5 = 2 6 ^ 5 = 3 9 ^ 5 = 12 合計 = 30(13 + 0 + 2 + 3
-
【C++】N人をMチームに分けたときの友人ペア数の最小値と最大値を求める方法
問題概要競技会に参加した N 人の参加者が、何らかの方法で M 個のチームに分けられました。ただし、各チームには必ず1人以上の参加者が所属するものとします。大会終了後、同じチームに所属していた参加者のペアはそれぞれ「友人」になります。このとき、大会終了までに形成され得る友人ペアの総数の最小値と最大値を求めるプログラムを作成するのが本記事の課題です。アルゴリズム(考え方)ペア数が最大になるケースペア数を最大化したいなら、できるだけ多くの人を1つのチームに集中させるのが最適です。具体的には、1つのチームに (n − m + 1) 人を入れ、残りの (m − 1) チームには各1人ずつ配分します。こ
-
C++で学ぶカウンター増分操作の償却分析:計算量を正しく理解する
償却分析(Amortized Analysis)とは、一連の操作に対して必要となる平均的な実行時間を求めるための解析手法です。これはアルゴリズムの平均ケース解析とは異なります。償却分析は常に平均ケースを想定するわけではなく、最悪ケースが発生する状況も考慮に入れます。そのため、償却分析は「一連の複数操作に対する最悪ケース解析」と捉えることができます。 一連の操作では、それぞれの操作にかかるコストが異なり、中には非常に高いコストがかかるものもあります。本記事では、この概念を理解するために、2進カウンター(バイナリカウンター)を例に挙げて解説します。 それでは、C++での動作と実装を見ながら、概念を
-
C++における挿入ソートの時間計算量をわかりやすく解説
挿入ソートの時間計算量とは?時間計算量とは、入力データの量を関数としたときに、一連のコードやアルゴリズムが処理・実行されるのにかかる時間のことを指します。挿入ソートの場合、最良ケースでは O(n)(ビッグオー・オブ・エヌ)のオーダーになります。一方、平均ケースおよび最悪ケースでは O(n2) のオーダーとなります。配列 6, 5, 8, 7, 10, 9 …(i と i-1 が入れ替わった形)に挿入ソートを適用した場合の時間計算量は?このような配列をソートする場合の時間計算量は O(n) です。この配列を詳しく観察すると、隣接する要素同士のペアがすべて元の位置から入れ替わっていることがわかりま
-
C++で連結リストを逆順に表示する面白い方法:キャリッジリターンを活用した1回走査のテクニック
連結リスト(リンクリスト)とは連結リストは、データ要素をリンクの形でつなげて格納するデータ構造です。各ノードは「データ要素」と「次のノードへのリンク」を持っています。連結リストを逆順に表示するという課題連結リストを逆順に表示することは、アルゴリズムの問題解決において頻出のテーマのひとつです。本記事では、C++を使って連結リストを逆順に表示する、ちょっと変わった興味深い方法を紹介します。一般的に、連結リストを逆順に表示するには、リストそのものを書き換えるか、複数回の走査(トラバース)を行う必要があります。しかし、今回紹介する方法ではそのような操作は一切不要で、リストの走査もたった1回で済みます。