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

PythonでGCDが1より大きい行列の連続要素の最大数を求めるプログラム

n行・m列の行列が与えられたとします。この中から、要素のGCD(最大公約数)が1より大きくなるような「連続する要素」の最大数を求めるのが今回の課題です。連続する要素は、行方向(水平)・列方向(垂直)のどちらに並んでいても構いません。

例として、次のような入力を考えてみましょう。

37912
5946
78510

m = 4、n = 3 のとき、出力は 3 になります。

その理由は、行列の第4列が「12, 6, 10」と並んでおり、これら3つの要素のGCDが 2(1より大きい)になるためです。該当する要素が3つあるので、答えは3となります。

解法のアプローチ

この問題は、次の手順で解くことができます。

  • mat := サイズ m × n × n の新しい3次元リストを用意する
  • res := 0(最終結果を保持する変数)
  • i を 0 から n-1 まで繰り返す
    • j を i から n-1 まで繰り返す
      • gcd_temp := 0、x := 0 で初期化する
      • k を 0 から m-1 まで繰り返す
        • i と j が等しい場合:mat[i][j][k] := input_list[i][k]
        • それ以外の場合:mat[i][j][k] := gcd(mat[i][j-1][k], input_list[j][k])
        • gcd_temp := gcd(gcd_temp, mat[i][j][k])
        • gcd_temp > 1 の場合:x := x + (j − i + 1)
        • それ以外の場合:res := max(res, x) とし、さらに mat[i][j][k] > 1 であれば gcd_temp := mat[i][j][k]、x := j − i + 1 とする
      • 内側のループを抜けたら res := max(res, x)
  • res を返す

実装例

理解を深めるために、実際の実装例を見てみましょう。

from math import gcd

def solve(n, m, input_list):
    # mat[i][j][k] := 行i〜行jのk列目の要素のGCD
    mat = [[[0] * m for _ in range(n)] for _ in range(n)]
    res = 0
    for i in range(n):
        for j in range(i, n):
            gcd_temp = 0
            x = 0
            for k in range(m):
                if i == j:
                    mat[i][j][k] = input_list[i][k]
                else:
                    mat[i][j][k] = gcd(mat[i][j-1][k], input_list[j][k])
                gcd_temp = gcd(gcd_temp, mat[i][j][k])
                if gcd_temp > 1:
                    x += j - i + 1
                else:
                    res = max(res, x)
                    if mat[i][j][k] > 1:
                        gcd_temp = mat[i][j][k]
                        x = j - i + 1
            res = max(res, x)
    return res

print(solve(3, 4, [[3, 7, 9, 12], [5, 9, 4, 6], [7, 8, 5, 10]]))

入力

3, 4, [[3, 7, 9, 12], [5, 9, 4, 6], [7, 8, 5, 10]]

出力

3

アルゴリズムのポイント

このアルゴリズムの鍵となるのは、行のペア (i, j) ごとに「行i〜行jの区間における列ごとのGCD」を累積的に再利用しながら計算している点です。mat[i][j][k] は「行iから行jまでのk列目の要素のGCD」を表し、ひとつ前の状態 mat[i][j-1][k] とのGCDを取るだけで更新できるため、無駄な再計算を避けられます。

また、gcd_temp が1より大きな値を保っている間は連続性が継続しているとみなし、GCDが1になった時点で走査をリセットします。これは「一度GCDが1になれば、そこに要素を追加してもGCDは1のまま」という性質を利用したものです。単一行(i = j)の走査では行内の水平方向の連続要素が、複数行にまたがる走査では列方向の連続要素が評価されます。全体の計算量は O(n² × m) 程度に収まります。

  1. Pythonで倉庫(godown)に押し込めるボックスの数を求めるプログラム

    問題の概要 2つの整数配列が与えられていると仮定しましょう。一方のリストには単位幅のボックスの高さが、もう一方の配列には倉庫(godown)内の各部屋の高さが格納されています。部屋には 0〜n の番号が付いており、各部屋の高さは godown 配列の対応するインデックスに記録されています。ここで、倉庫に押し込むことのできるボックスの数を求めます。 ただし、以下のルールを守る必要があります。 ボックスを積み重ねることはできません。 ボックスの順序は自由に入れ替えられます。 ボックスは必ず左から右へ向かって挿入します。 もしボックスの高さがある部屋の高さより大きい場合、そのボックスおよびそれよ

  2. Pythonで行列の転置を求めるプログラム

    この記事では、与えられた問題に対する解法とアプローチについて詳しく解説します。 問題文 ある行列が与えられたとき、その転置を同じ行列に格納し、結果を表示する必要があります。 行列の転置とは、行を列に、列を行に入れ替えたものです。言い換えれば、行列Aの転置は、要素A[i][j]をA[j][i]と入れ替えることで得られます。 実装例 N = 4 def transpose(A): for i in range(N): for j in range(i+1, N): A[i][j], A[j][i] = A[j][i], A[i][j] # ドライ