質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

3回答

1748閲覧

python 辞書 値 ○個単位で最小値を求める

zeitaku_fire

総合スコア26

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2018/06/29 10:51

編集2018/06/29 10:55

python

1num = [i for i in map(int,input().split(" "))] #入力データの一行目取り出し [7, 3] 2 3date=[] 4uryo=[] 5for i in range(num[0]): #7回繰り返し 6 7 8 dater=[i for i in map(int,input().split(" "))] #入力データをリストとして取り出し 9 date.append(dater[0]) 10 uryo.append(dater[1]) 11 player_ans = {} 12 for h, p in zip(date, uryo): #取り出した入力データをリストに格納 13 player_ans[h] = p 14 15#上記6行に渡って、「入力データ取り出し ⇒ リスト ⇒辞書」としています。 16#下記の1行でできると思ったのですが、上手く動かなかったので、やむを得ず上記のコードとしています。 17#player_ans = {date:uryo for date,uryo in input().split(" ")} 動かない 18 19print(player_ans) #{1: 0, 2: 20, 3: 10, 4: 80, 5: 10, 6: 0, 7: 10} 20 21#入力データ 227 3 #7は下記入力データの数を表している。 3は連続する回数を表している。 ※詳しくは下記に。 231 0 242 20 253 10 264 80 275 10 286 0 297 10 30#希望する出力 5

『やりたいこと』
・入力データの2行目(1,0)からを対象に、右の数字(今回の場合は3個単位 ※最初の入力データの3をみる)を合計。
最も低い合計の行を出力したい。

 今回の入力データの場合、まず
1 0
2 20
3 10
これらの右の数を合計(30)

 次に
2 20
3 10
4 80
これらの右の数を合計(110)

 3 10
4 80
5 10
これらの右の数を合計(100)

 と、最後までみていき、
合計が最も低い行を出力したい。
今回の入力データでは、
5 10
6 0
7 10
が、合計20となるため、この合計値の先頭(5)を出力

3個単位で合計を見る必要があるため、例えば先頭の(1,0)が最小(値0)ということで出力するのはNG
最後の(6,0 7,10)が最小(値10)ということで出力するのもNG(2行しかみていないため)

『考えたこと』
・辞書型にして、行をkeyに、値をvalueに、値を3個単位で合計して最小値を求めるのが楽かと思ったんですが、
どのようなコードで計算すればいいかつまづいてしまいました。
※載せているコードは、入力データを辞書化しただけです。

『ご質問』
・辞書型で解く方法、もしくは別なやり方がありましたら、ご教授いただきたいです。
・今回、入力データをリスト化して、辞書型としていますが、
もっと楽に辞書型にできるようでしたら、こちらもご教授いただきたいです。 

ここまで長くなってごめんなさい。
よろしくお願いします。

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

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

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

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

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

guest

回答3

0

回答の前にひとつアドバイスしますと、

python

1 player_ans = {} 2 for h, p in zip(date, uryo): #取り出した入力データをリストに格納 3 player_ans[h] = p

これをforの中に入れるのはやめましょうか(無駄だから)。


どうせ辞書に入れるなら、もうすこし旨味のある入れ方をした方が良いでしょう。

python

1# めんどいので入力周り省略 2num = [7, 3] 3date = [1,2,3,4,5,6,7] 4uryo = [0,20,10,80,10,0,10] 5 6dn, span = num # 扱いづらいのでunpack 7player_ans = {} 8for i in range(dn - span + 1): # このforループの意味は頭をひねって考えること 9 player_ans[date[i]] = uryo[i:i+span] 10 11# 以下3つは確認用のprint 12print(player_ans) # => {1: [0, 20, 10], 2: [20, 10, 80], 3: [10, 80, 10], 4: [80, 10, 0], 5: [10, 0, 10]} 13print(player_ans.items()) # => dict_items([(1, [0, 20, 10]), (2, [20, 10, 80]), (3, [10, 80, 10]), (4, [80, 10, 0]), (5, [10, 0, 10])]) 14print(min(player_ans.items(), key=lambda x:sum(x[1]))) # => (5, [10, 0, 10]) 15 16# 最終的にほしいもの 17print(min(player_ans.items(), key=lambda x:sum(x[1]))[0]) # => 5 18 19# こっちでもいいかなぁ 20print(min(player_ans, key=lambda x:sum(player_ans[x]))) # => 5

あるいは、

python

1for i in range(dn - span + 1): # このforループの意味は頭をひねって考えること 2 player_ans[date[i]] = sum(uryo[i:i+span])

で後ろのminのkeyが単純になります。後からリストを使わないのならこの方が良いかも。

投稿2018/06/29 11:38

編集2018/06/29 13:12
hayataka2049

総合スコア30933

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

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

zeitaku_fire

2018/06/29 16:12

いつもありがとうございます。 下記のように理解しました。誤ってましたらご指摘お願いします。 ただ、疑問が2点あります。 >>dn, span = num # 扱いづらいのでunpack ⇒numの値をdn=7 span=3へ 解釈。 >>for i in range(dn - span + 1): # このforループの意味は頭をひねって考えること >> player_ans[date[i]] = uryo[i:i+span] ⇒まず、繰り返し条件の「dn - span + 1」  これは、数値にすると5になる  なぜ5かというと、7個の数値を3個単位で合計する場合、必要なリストの数は合計5となることから、この計算式としている。 次に「player_ans[date[i]] = uryo[i:i+span]」 左の部分「player_ans[date[i]]」、date[i]としていることから、keyを指定されていると想定。 右の部分「uryo[i:i+span]」、uryoはリスト化されていて、[i:i+span]というのは、リストの○番目から○番目を抽出して、keyにリストとして値をもたせている。 『疑問』 2点あります。 ■for i in range(dn - span + 1) 上記の場合、iは0からはじまります。現に、iをprintしてみたら、0,1,2・・・となっていました。 しかし、下記コードのdate[i]は1,2,3・・・となっています。 player_ans[date[i]] = uryo[i:i+span]  i+1という表記はどこにもないはずなのに、なぜ、1がはいるのでしょうか? ■print(min(player_ans.items(), key=lambda x:sum(x[1]))[0]) # => 5 前半部分「min(player_ans.items()」ここの意味は、player_ansは辞書化されていて、値には3個ずつにまとめたリストが入っている。minを指定しているから、最小値のkeyとvalueがitemsにより選択されると解釈。 その先のlambda「key=lambda x:sum(x[1]))[0])」について、 xに前半部分のminで指定したkeyとvalueが引数と渡されて、:の後の処理を行っているのだと想定したのですが、 key=とsum(x[1]))[0])これは何をやっているのでしょうか。。?
hayataka2049

2018/06/30 03:10

疑問1:date = [1,2,3,4,5,6,7]だから。date[0]は1 疑問2::sum(x[1]))[0])という処理はしていません。3つめの確認用printで出ている結果から、キーだけ取り出してるのが後ろの[0]で、これはminの外にあります あとはすべてminの中で完結しています。minのkeyについては理解しづらかったら調べると解説記事があると思います
zeitaku_fire

2018/06/30 04:34

ご教授ありがとうございます。 keyについて、理解が深まりました。 記事も目を通しました。 今後ともよろしくお願いします。
guest

0

ベストアンサー

個人的には、辞書を使わない方が書きやすい気がします。

解法の方針
0. 3個ずつ組にしたタプルのリストを作る
0. 最も合計値が小さいタプルのインデックスを調べる

Python

1n, span = map(int, input().split()) 2 3nums = [ 4 int(input().split()[-1]) for _ in range(n) 5] 6 7groups = zip(*[nums[s:] for s in range(span)]) 8indice = [ 9 index 10 for index, group in sorted( 11 enumerate(groups, start=1), 12 key=lambda ig: sum(ig[-1]) # (index, num_group) 13 ) 14] 15 16print(indice[0])

Wandbox


hayataka2049さんのご指摘を受けて修正したバージョン。

Python

1n, span = map(int, input().split()) 2 3nums = [ 4 int(input().split()[-1]) for _ in range(n) 5] 6 7groups = zip(*[nums[s:] for s in range(span)]) 8index, _ = min( 9 enumerate(groups, start=1), 10 key=lambda ig: sum(ig[-1]) # (index, num_group) 11) 12 13print(index)

Wandbox

せっかくなので

辞書を使った方法も書いてみた。

Python

1n, span = map(int, input().split()) 2 3nums = [ 4 int(input().split()[-1]) for _ in range(n) 5] 6 7sum_dict = { 8 i: 0 for i in range(n-span+1) 9} 10for key in sum_dict: 11 for i in range(span): 12 sum_dict[key] += nums[key+i] 13 14index = sorted(sum_dict.items(), key=lambda item: item[-1])[0][0] 15print(index+1)

Wandbox

コメントを受けて

enumerateなんですが、for文の際に使うもので、リストのインデックスをとるときに使用するものだと認識しておりますが、for文がありません。

ここでのenumerateは何をしているのでしょうか。。?

また、groupsは上記でタプルにされているデータをさしているのはわかるのですが、stard=1は何なのでしょうか?

for文で良く用いられるのは確かですが、その実態はジェネレータを返しているだけなのです。
また、キーワード引数startは、どの数値から順に付与するかを示します。

Python

1>>> abc = list('abcdefg') 2>>> abc 3['a', 'b', 'c', 'd', 'e', 'f', 'g'] 4>>> 5>>> enumerate(abc) 6<enumerate object at 0x00000270D21ED438> 7>>> 8>>> list(enumerate(abc)) 9[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f'), (6, 'g')] 10>>> 11>>> list(enumerate(abc, start=5)) 12[(5, 'a'), (6, 'b'), (7, 'c'), (8, 'd'), (9, 'e'), (10, 'f'), (11, 'g')]

key=lambda ig: sum(ig[-1])

lambdaは、引数、:の後に処理を記述すると認識しております。
min(enumerate(groups, start=1)この処理結果がigにはいって、sumして、keyに入って、それをindexと_に戻り値として渡される?という認識でよろしいでしょうか?

enumerate(groups, start=1)が返した各要素がigに入ります。
例えば一回目は(1, (0, 20, 10))が、二回目は(2, (20, 10, 80))がigに与えられるわけです。

keyは関数が『何をもって要素を比べるか』指定するために使います。
今回はsum(ig[-1])、つまり件の三個組の要素の合計を指標にしています。

minした結果をラムダで処理しているのではなく、minを求めるのにラムダを用いています。
minがどんな処理をしているか覗いてみたら、少しイメージが湧くかもしれません。

Python

1def my_min(it, key=None): 2 print('-' * 42) 3 if key is None: 4 key = lambda x: x # 恒等関数 5 6 ret = next(it) 7 ret_score = key(ret) 8 9 for e in it: 10 e_score = key(e) 11 print(f'{ret}[score={ret_score}] vs {e}[score={e_score}]', end='\t') 12 13 if ret_score > e_score: 14 print('latter wins') 15 ret, ret_score = e, e_score 16 else: 17 print('former wins') 18 19 print(f'smallest element is {ret}') 20 print('-' * 42) 21 return ret

実行結果 (minの代わりにmy_minを使用) Wandbox

plain

1------------------------------------------ 2(1, (0, 20, 10))[score=30] vs (2, (20, 10, 80))[score=110] former wins 3(1, (0, 20, 10))[score=30] vs (3, (10, 80, 10))[score=100] former wins 4(1, (0, 20, 10))[score=30] vs (4, (80, 10, 0))[score=90] former wins 5(1, (0, 20, 10))[score=30] vs (5, (10, 0, 10))[score=20] latter wins 6smallest element is (5, (10, 0, 10)) 7------------------------------------------ 85

註: 説明のために書いたコードであり、実際のminの実装を踏襲したものではありません。


前半部分「sorted(sum_dict.items()」は、辞書をソートされている。ここまではわかるのですが、

やはり、lambdaの処理が完璧に理解できません。(ソートした辞書をitemに渡す?)

辞書の各要素をタプルにしたシーケンスを作り、そいつをソートしています。
ソートした結果をラムダで処理しているのではなく、ソートするのにラムダを用いています。

Python

1>>> tmp_dict = {i: i**2 for i in range(10)} 2>>> tmp_dict 3{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} 4>>> 5>>> tmp_dict.items() 6dict_items([(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49), (8, 64), (9, 81)])

回答いただいたのに、それに対しての質問ばかりでごめんなさい。

いえいえ、お気遣いなく。

投稿2018/06/29 11:21

編集2018/06/30 04:37
LouiS0616

総合スコア35660

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

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

hayataka2049

2018/06/29 11:53 編集

この手のコードを見るたびに思いますが、sortせずに済ませるとO(nlogn)がO(n)になってお得です(どうでも良いけど)
LouiS0616

2018/06/29 11:58

拡張性をある程度担保しようという考えが根底にはあります。 が、ただの手癖になってしまっているのが正直なところで、今回も特に意識しないでminを避けsortedを使ってしまいました。 ご指摘ありがとうございます。
zeitaku_fire

2018/06/29 18:13

いつもご教授いただきありがとうございます。 下記の理解で誤りがありましたらご指摘お願いします。 ただ、疑問があります。(全部で4個) まず、2番目に作成いただいたコードなのですが、 >>nums = [int(input().split()[-1]) for _ in range(n)]  ⇒これで入力データの右側をリスト。(1でも右側を取り出せる)   ※-1を0にすると、左側だけ取り出せる。   for _ in range   _(アンダーバー)というのは、変数を使わないということ   ※for i in rangeとしても同じ動きをする、はず >>groups = zip(*[nums[s:] for s in range(span)])  ⇒リストを3個単位でタプルにしている。   3個というのは、spanの3である。 >>index, _ = min(enumerate(groups, start=1),key=lambda ig: sum(ig[-1]) # (index, >>num_group) >>)  index, _は、戻り値がふたつあるから、受け皿を二つ用意。  必要なのはindexに入る値のみ。_は使用しない? 『疑問』 >>index, _ = min(enumerate(groups, start=1),key=lambda ig: sum(ig[-1]) # (index, >>num_group) >>)  minの意味はわかります。※最小値をとる ① enumerateなんですが、for文の際に使うもので、リストのインデックスをとるときに使用するものだと認識しておりますが、for文がありません。 ここでのenumerateは何をしているのでしょうか。。? ②また、groupsは上記でタプルにされているデータをさしているのはわかるのですが、stard=1は何なのでしょうか? ③key=lambda ig: sum(ig[-1]) lambdaは、引数、:の後に処理を記述すると認識しております。 min(enumerate(groups, start=1)この処理結果がigにはいって、sumして、keyに入って、それをindexと_に戻り値として渡される?という認識でよろしいでしょうか? ====2番目のコードについてはここまで==== 3番目の辞書でやって頂いたコードなのですが、 下記のコード以外は、何をやっているのか理解しました。 >>index = sorted(sum_dict.items(), key=lambda item: item[-1])[0][0] ④ 前半部分「sorted(sum_dict.items()」は、辞書をソートされている。ここまではわかるのですが、 やはり、lambdaの処理が完璧に理解できません。(ソートした辞書をitemに渡す?) 回答いただいたのに、それに対しての質問ばかりでごめんなさい。
LouiS0616

2018/06/30 04:38

@zeitaku_fire さん ご質問の点について追記しましたので、ご確認ください。
zeitaku_fire

2018/06/30 05:37

ありがとうございます。追記頂いた分確認しました。 ご丁寧にコードも作成頂き、イメージを昨日より持つことができました。 完全に理解するにはまだ時間がかかりますが、今後ともご教授のほどよろしくお願いします。 ありがとうございました。
guest

0

愚直だけど、こんな感じでもできるかもです。
積算値をキュー的に扱い(前の値を引き現在の値を足す)、計算量はO(n)でいけてると思います。

Python

1import random 2random.seed(110) 3cnt = 10 4l = [random.randrange(cnt*2) for i in range(cnt)] # rangeも個数に合わせる。(0が連続しないように) 5#print(l) 6span = 3 7 8# 0番目からspan個を足す。とりあえずこれを最小値とする。 9cur_sum = 0 10for i in range(span): 11 cur_sum += l[i] 12#print(cur_sum) 13cur_sub = l[0] 14min_sum = cur_sum 15min_pos = 0 16 17# 1番目から末尾を足すまで 18for i in range(1,cnt-span+1): 19 # 前の値を引いて、現在の値を足す=現在の積算値 20 cur_sum -= cur_sub 21 cur_add = l[i+span-1] 22 cur_sum += cur_add 23 #print('cur pos={} sub={} add={} sum={}'.format(i,cur_sub,cur_add, cur_sum)) # 24 cur_sub = l[i] 25 if cur_sum <= min_sum: 26 min_sum = cur_sum 27 min_pos = i 28 #print('min pos={} cur_min={}'.format(min_pos,min_sum)) 29 30print( 'total_min pos={} sum={}'.format(min_pos, min_sum))

境界値をちゃんと考慮できていないかも。

投稿2018/06/29 13:04

編集2018/06/29 13:10
can110

総合スコア38262

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

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

zeitaku_fire

2018/06/29 16:31

ありがとうございます。 ほかのアプローチを思いつかなかったので、ご教授いただいたコード、参考にさせて頂きます。 今後ともよろしくお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問