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

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

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

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

Q&A

解決済

3回答

276閲覧

指定した自然数のうち約数が指定した数あるか求める計算をはやくしたい

TanakashiXr

総合スコア57

Python 3.x

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

0グッド

0クリップ

投稿2018/12/22 07:10

編集2018/12/22 07:18

前提・実現したいこと

指定した自然数(max)のうち約数が指定した数(hit)ある計算をするプログラムを作りました。
ですが、とても遅いと感じもっとはやくできないのかと思いました。

該当のソースコード

import sys, time max = 114514 count = 0 count1 = 0 hit = 13 seikai = [] start = time.time() print(str(max) + "つのうちの、約数が" + str(hit) + "つある数を調べます") def keisan(): global count global count1 count += 1 for i in range(1,count): if count % i == 0: count1 += 1 while not count == max: if not count1 == hit - 1: print(str(count) + "は" + str(count1) + "のため再計算します") count1 = 0 keisan() else: print(str(count) + "は正解です") seikai.append(count) count1 = 0 keisan() else: if not seikai: print("正解はありませんでした") else: elapsed_time = time.time() - start print ("elapsed_time:{0}".format(elapsed_time) + "[sec]") print("終了しました\n以下が正解です" + str(seikai))

試したこと

Numbaを使用しようとしましたが、global変数を使っているためエラーが出てだめでした。

前提として、Python3でお願いします。何か無駄な部分等あればご教授よろしくお願いいたします。

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

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

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

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

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

LouiS0616

2018/12/22 07:18

『素数が指定した数ある』と『約数が指定した数ある』のどちらが正しいのですか?
TanakashiXr

2018/12/22 07:19

申し訳ございません。 約数に修正しました。
guest

回答3

0

約数の個数が 13 (奇数) ということから、 平方数だけをしらべればよいことになります。
( n に対して p が 約数なら、 n / p も 約数になる。
約数の個数が奇数になるのは p = n / p. つまり n = p * p のかたちのときだけ)

このことを治療して、プログラムしてみました。
探す範囲も広げてみました。

c2.py

python3

1import time 2import math 3 4def is_squre(num): 5 sqrt =int(math.sqrt(num)) 6 return num == sqrt * sqrt 7 8def keisan(num): 9 d = [1, num] 10 for i in range(2, 1 + int(math.sqrt(num))): 11 if num % i == 0: 12 d += [i, num // i] 13 return len(set(d)) 14 15max = 999999999999 # 114514 16hit = 13 17seikai = [] 18 19print("1 .." + str(max) + " 中の約数が" + str(hit) + "つある数を調べます") 20 21start = time.time() 22 23if hit == 1: 24 # 約数の個数が 1 なのは 1 だけ 25 seikai = [1] 26else: 27 # 約数の数が奇数なのは、平方数に限る 28 seikai = [num for num in range(2, int(math.sqrt(max)) + 1) if is_squre(num) and keisan(num) == hit] 29 30elapsed_time = time.time() - start 31print ("elapsed_time:{0}".format(elapsed_time) + "[sec]") 32 33if not seikai: 34 print("正解はありませんでした") 35else: 36 print("終了しました\n以下が正解です" + str(seikai)) 37 print(len(seikai))

実行例
イメージ説明

参考情報

  • 数の性質 4096

https://ja.numberempire.com/4096

  • 数の性質 531441

https://ja.numberempire.com/531441

投稿2018/12/25 12:52

編集2018/12/25 13:17
katoy

総合スコア22324

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

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

0

ベストアンサー

global変数を使った時に、エラーがでてだめならば、global変数を除去してみてはいかがでしょうか? 上記のプログラムでcountとcount1をglobal変数にもたせておく必要性は特に無いかなと思いました。

countは、約数の数を調べるターゲットとなる数、count1は、countの約数の数だと思いますが、それぞれ、countは引数として関数に渡して、count1は、戻り値として元の処理に返してしまえば、いいと思います。

参考までに、numbaを使って、以下のように書きなおしてみました。(print文の箇所は大量に出力されると困るので、一部コメントアウトしています。)

python

1import sys, time, numba 2 3max = 114514 4hit = 13 5seikai = [] 6 7start = time.time() 8print(str(max) + "つのうちの、約数が" + str(hit) + "つある数を調べます") 9 10@numba.jit 11def keisan(count): 12 count1 = 0 13 for i in range(1,count): 14 if count % i == 0: 15 count1 += 1 16 return count1 17 18count = 0 19count1 = 0 20while not count == max: 21 count1 = keisan(count) 22 if not count1 == hit - 1: 23 # print(str(count) + "は" + str(count1) + "のため再計算します") 24 pass 25 else: 26 # print(str(count) + "は正解です") 27 seikai.append(count) 28 count += 1 29else: 30 if not seikai: 31 print("正解はありませんでした") 32 else: 33 elapsed_time = time.time() - start 34 print ("elapsed_time:{0}".format(elapsed_time) + "[sec]") 35 print("終了しました\n以下が正解です" + str(seikai)) 36

余談ですが、約数を数える上でのトリックとして、1〜countの値全ての数について割り切れるかどうかを判定する必要はありません。count/2+1〜count-1までの範囲には、countの約数は存在しないので、この範囲の値については調べなくても良いのです。

1 -> 1
2 -> 1, 2
3 -> 1, 3
4 -> 1, 2, 4
5 -> 1, 5
6 -> 1, 2, 3, 6
...
12 -> 1, 2, 3, 4, 6, 12
13 -> 1, 13
14 -> 1, 2, 7, 14
15 -> 1, 3, 5, 15
16 -> 1, 2, 4, 8, 16

などと、書いていけば分かると思いますが、基本的に2で割り切れた場合、その数の自分自身を除いく最大の約数は、2で割った値になります。また、2で割り切れなかった場合も、2で割った値を四捨五入した値以上の約数になることは、約数の性質上ありえません。

なので、実は、keisanの処理の箇所は以下のように書きなおす事ができます。

python

1def keisan(count): 2 count1 = 0 3 for i in range(1,int(count/2) + 1): 4 if count % i == 0: 5 count1 += 1 6 return count1

さらにこの辺の約数の性質を上手く使うと、さらに効率よく約数を求めていく処理が書けると思います。この辺の高速化の処理を考えてみるのも面白いと思いますので、是非チャレンジしてみてください!

投稿2018/12/22 10:24

yuwki0131

総合スコア160

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

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

TanakashiXr

2018/12/22 20:59

global変数の除いてnumbaを使ったサンプルにして約6倍になりました! ありがとうございました!高速化に向けて頑張りたいと思います!
guest

0

全体を書き直してみました。
そして質問にあるコードの途中の print 文を削除、最後に seikai の個数を出すようにしたものと速度を比較してみました。

c1.py

python

1import time 2import math 3 4def keisan(num): 5 d = [1, num] 6 for i in range(2, 1 + int(math.sqrt(num))): 7 if num % i == 0: 8 d += [i, num // i] 9 return len(set(d)) 10 11max = 114514 12hit = 13 13seikai = [] 14 15print("1 .." + str(max) + " 中の約数が" + str(hit) + "つある数を調べます") 16 17start = time.time() 18 19if hit == 1: 20 # 約数の個数が 1 なのは 1 だけ 21 seikai = [1] 22else: 23 seikai = [num for num in range(2, max + 1) if keisan(num) == hit] 24 25elapsed_time = time.time() - start 26print ("elapsed_time:{0}".format(elapsed_time) + "[sec]") 27 28if not seikai: 29 print("正解はありませんでした") 30else: 31 print("終了しました\n以下が正解です" + str(seikai)) 32 print(len(seikai)) 33

20000 までの場合と、114514 までの場合で処理時間を比較してみました。

実行例
イメージ説明

イメージ説明

投稿2018/12/23 04:15

katoy

総合スコア22324

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問