🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

4回答

888閲覧

数値のリストの中身を足し合わせた結果が特定の値になったときの処理

aufheben

総合スコア24

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/12/22 14:17

python3を使用しています。

例えば数値のリストlst[2, 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 4, 1, 4, 1, 2, 1, 3, 1, 4, 1, 3, 1, 2, 1, 6, 2, 1, 2, 1, 2, 1, 2, 1,]
があったとします。
このとき左から足していって3,6,4の並びになった時にTrueを返すようなプログラムを実現したいです。すこし特殊な状況なので人力で処理を書きます。
この場合だと、

2+1=3...第1段階クリア(lst[0]+lst[1]==3) 1+2+1+3>6...6を超えているのでNG(lst[2]+lst[3]+lst[4]+lst[5]) 次はlst[1]からやり直し 1+1+2>3...3を超えているのでNG(lst[1]+lst[2]+lst[3]) 次はlst[2]から 1+2=3...第1段階クリア(lst[2]+lst[3]==3) 1+3+2=6...第2段階クリア(lst[4]+lst[5]+lst[6]==6) 1+3=4...第3段階クリア(lst[7]+lst[8]==4) この時Trueを返す(インデックスも) 次はlst[3]から以下同様最後まで

以上のようなプログラムです。
単に3+6+4で左からまとめて足して13になっていればよいというわけではなく、必ず3,6,4の区切りである必要があります。
自分なりに少し考えてみたのですが、ループ処理中の配列のインデックス指定や、失敗した際に次の要素からやり直すこと、ループ回数が増えて処理が重くなる、同じ計算をしている?等々色々な要素が入り混じっていてよくわからなくなってしまいました。

python、アルゴリズム共に初学者で余り明るくないのでご教授いただければと思います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

数値のリストに正の数しか含まれない前提で良ければ、累積和を使ったアプローチが使えると思います。

まず、数値リスト(lst)の先頭から累積和を計算して、辞書の形(acc)で用意しておきます。
lstの先頭から順に足していって 3, 6, 4 の並びになるということは、累積和で 3, 9, 13 パタンが出現するということになります。
acc中に 3, 9, 13 が全てあれば、lst[0]から足していったときに 3, 6, 4のパタンがあったことになります。

lst[1]から足していったときに 3, 6, 4のパタンがあるかをチェックするために
累積和の辞書(acc)を作り直すと時間がかかってしまうため、
代わりに 3+lst[0], 9+lst[0], 13+lst[0] が出現するかでチェックします。

同様に、lst[n]から足していったときに 3, 6, 4 の並びがあるかは、
3, 9, 13 それぞれにsum(lst[0:n])を足した数字があるかでチェックできます。
この3, 9, 13に足す数値をoffsetで管理することで、累積和やsumを何度も計算するのを避けることができます。

python

1 2from itertools import accumulate 3 4# 以下のように0や負の数が含まれる場合には、このプログラムは期待通りに動作しません 5# lst = [1, 2, 1, 3, 2, 1, 3, 0] 6# lst = [13, -4, -5, -1] 7def solve(lst, targets): 8 acc = {a: i for i, a in enumerate(accumulate(lst))} 9 numbers = list(accumulate(targets)) 10 offset = 0 11 res = [] 12 for i, l in enumerate(lst): 13 if all(n+offset in acc for n in numbers): 14 res.append((i, acc[numbers[-1]+offset]+1)) # 3, 6, 4 になる部分の始点と終点を記録 15 offset += l 16 return res 17 18 19lst = [2, 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 4, 1, 4, 1, 2, 1, 3, 1, 4, 1, 3, 1, 2, 1, 6, 2, 1, 2, 1, 2, 1, 2, 1] 20res = solve(lst, (3, 6, 4)) 21for l, r in res: 22 print(f'lst[{l}:{r}] => {lst[l:r]}') 23# lst[2:9] => [1, 2, 1, 3, 2, 1, 3] 24# lst[6:12] => [2, 1, 3, 2, 1, 4] 25# lst[17:23] => [3, 1, 4, 1, 3, 1] 26 27 28# (3, 6, 4)以外のパタンも探せます 29res = solve(lst, (3, 8)) 30for l, r in res: 31 print(f'lst[{l}:{r}] => {lst[l:r]}') 32# lst[5:10] => [3, 2, 1, 3, 2] 33# lst[8:13] => [3, 2, 1, 4, 1] 34# lst[15:20] => [2, 1, 3, 1, 4] 35# lst[23:27] => [2, 1, 6, 2] 36# lst[26:33] => [2, 1, 2, 1, 2, 1, 2]

投稿2020/12/22 17:38

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

プログラムは少し長くなりますが、パターンを使うことで高速にできる可能性があります。
このアルゴリズムをnumpyのndarrayでやるとかなり速いと思います。

python

1#パターン作成 2 3def enhance(l): 4 lcopy = list(l) 5 lcopy.append(1) 6 return lcopy 7 8def increase(l): 9 lcopy = list(l) 10 lcopy[-1] += 1 11 return lcopy 12 13def make_pattern(n): 14 if n == 1: 15 return [[1]] 16 else: 17 spattern = make_pattern(n-1) 18 return [increase(l) for l in spattern] + [enhance(l) for l in spattern] 19 20pattern3 = make_pattern(3) 21pattern4 = make_pattern(4) 22pattern6 = make_pattern(6) 23 24#パターン作成終了 25 26def check(lst, pattern): 27 for p in pattern: 28 if lst[:len(p)] == p: 29 return len(p) 30 return False 31 32def check_head(lst): 33 result3 = check(lst, pattern3) 34 if result3 == False: 35 return False 36 else: 37 pass 38 result6 = check(lst[result3:], pattern6) 39 if result6 == False: 40 return False 41 else: 42 pass 43 result4 = check(lst[result3+result6:], pattern4) 44 if result6 == False: 45 return False 46 else: 47 return (result3, result3+result6, result3+result6+result4) 48 49def check_all(lst): 50 for i in range(len(lst)): 51 result_head = check_head(lst[i:]) 52 if result_head != False: 53 return (result_head[0]+i, result_head[1]+i, result_head[2]+i) 54 return False 55

実行すると、

python

1>>> lst = [2, 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 4, 1, 4, 1, 2, 1, 3, 1, 4, 1, 3, 1, 2, 1, 6, 2, 1, 2, 1, 2, 1, 2, 1,] 2>>> 3>>> check_all(lst) 4(4, 7, 9)

ちなみに、パターンを表示すると以下のようになります。

python

1>>> print(pattern3) 2[[3], [1, 2], [2, 1], [1, 1, 1]] 3>>> print(pattern4) 4[[4], [1, 3], [2, 2], [1, 1, 2], [3, 1], [1, 2, 1], [2, 1, 1], [1, 1, 1, 1]] 5>>> print(pattern6) 6[[6], [1, 5], [2, 4], [1, 1, 4], [3, 3], [1, 2, 3], [2, 1, 3], [1, 1, 1, 3], [4, 2], [1, 3, 2], [2, 2, 2], [1, 1, 2, 2], [3, 1, 2], [1, 2, 1, 2], [2, 1, 1, 2], [1, 1, 1, 1, 2], [5, 1], [1, 4, 1], [2, 3, 1], [1, 1, 3, 1], [3, 2, 1], [1, 2, 2, 1], [2, 1, 2, 1], [1, 1, 1, 2, 1], [4, 1, 1], [1, 3, 1, 1], [2, 2, 1, 1], [1, 1, 2, 1, 1], [3, 1, 1, 1], [1, 2, 1, 1, 1], [2, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]]

投稿2020/12/22 16:48

ppaul

総合スコア24670

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

条件を満たす部分リストを列挙するプログラムをかいてみました。
列挙結果の個数が 0 かどうかで True/False 判定するればよいだけなので。

p.py

python3

1# 連続する要素の部分和が n になる場所を列挙する 2def sublist(lst, n): 3 ans = [] 4 for i in range(len(lst)): 5 for length in range(1, len(lst) - i): 6 j = i + length - 1 7 if (sum(lst[i:j])) == n: 8 ans.append([lst[i:j], lst[j:]]) 9 return ans 10 11# 先頭の連続する要素の和が n になるものを列挙する 12def headlist(lst, n): 13 ans = [] 14 for i in range(len(lst)): 15 if sum(lst[0:i]) == n: 16 ans.append([lst[0:i], lst[i:]]) 17 return ans 18 19lst = [ 20 2, 1, 1, 2, 1, 3, 2, 1, 3, 2, 21 1, 4, 1, 4, 1, 2, 1, 3, 1, 4, 22 1, 3, 1, 0, 2, -2,1, 6, 2, 1, 2, 1, 23 2, 1, 2, 1 24] 25 26ans1 = sublist(lst, 3) 27ans2 = [] 28for x in ans1: 29 for y in headlist(x[1], 6): 30 ans2.append([x[0] + y[0], y[1]]) 31 32ans3 = [] 33for x in ans2: 34 for y in headlist(x[1], 4): 35 ans3.append([x[0] + y[0], y[1]]) 36 37for z in ans3: 38 print(z[0]) 39

質問文にあった数列に 0, 2, -2 を挿入しています。
実行結果では、これを含む 部分リストが列挙されることが確認できています。

実行例:
イメージ説明

ここでは同じ計算を避ける代わりに、大量のメモリをつかっています。
2番目以降の和のチェックをする部分は再帰をつかって書くこともできるかもしれません。
(そうすれば、和のパターンを配列で渡してチェックプログラムを書くことができそうです)

投稿2020/12/23 22:36

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

処理を分ければ見通しが良くなります。

Python

1def checksub2(lst,x): 2# リストの先頭から幾つか足すとその数になるか? 3 s = 0 4 for i, v in enumerate(lst): 5 s += v 6 if s == x: 7 return i+1 8 if s > x: 9 return False 10 return False 11 12def checksub(lst): 13# リストの先頭からが条件を満たすか? 14 return ( 15 (n1 := checksub2(lst,3)) and 16 (n2 := checksub2(lst[n1:],6)) and 17 checksub2(lst[n1+n2:],4) 18 ) 19 20def check(lst): 21 for i in range(0,len(lst)): 22 if checksub(lst[i:]): 23 return i 24 return False 25 26print(check([2, 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 4, 1, 4, 1, 2, 1, 3, 1, 4, 1, 3, 1, 2, 1, 6, 2, 1, 2, 1, 2, 1, 2, 1,]))

Python3.8以降でない場合。

Python

1def checksub(lst): 2 n1 = checksub2(lst,3) 3 if n1: 4 n2 = checksub2(lst[n1:],6) 5 if n2: 6 return checksub2(lst[n1+n2:],4) 7 return False

⇒ 代入式無いとたるい感じですね。

#追記
求めるパターン(3,6,4)がコード中に埋め込まれているのは、やな感じなので、引き数で渡すことにします。

Python

1def checksub2(lst,x): 2 s = 0 3 for i, v in enumerate(lst): 4 s += v 5 if s == x: 6 return i+1 7 if s > x: 8 return False 9 return False 10 11def checksub(lst, pattern): 12 i = 0 13 for n in pattern: 14 if not (j := checksub2(lst[i:],n)): 15 return False 16 i += j 17 return True 18 19def check(lst, pattern): 20 for i in range(0,len(lst)): 21 if checksub(lst[i:],pattern): 22 return i 23 return False 24 25print(check([2, 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 4, 1, 4, 1, 2, 1, 3, 1, 4, 1, 3, 1, 2, 1, 6, 2, 1, 2, 1, 2, 1, 2, 1,], 26 [3,6,4]))

分かりやすさ優先で書いてますので、速度を求めるならスライスを渡さず、「リスト全体と開始添え字」を渡すといいです。

投稿2020/12/22 15:24

編集2020/12/22 17:45
otn

総合スコア85893

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問