こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

回答受付中の質問

pythonで桁の大きな数字を調べる方法

pythonですが、簡単なプログラムで計算しても10000!とかが計算できてしまいます。コンピュータで使える数字の桁数って限定されると思っていたのですが、pythonだと計算できるのでしょうか。
なお、ちゃんと計算できているのかどうか不明ではありますが。

また、ものすごく桁数が長い数字とか無理数とかですが、数字(0-9)が表れる頻度を調べることができないかと思いますが、プログラムを組むことは可能でしょうか。円周率でも平方根、ネイピア数とかでもですが。かなりイーブンになるものでしょうか。10000!とかだと原理的に0が多くなるようですが。

よろしくお願いします。

投稿日時 - 2018-09-25 07:00:42

QNo.9540886

困ってます

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.3

これはおそらく目の付け方に間違いがあります。

確かにIntegerとかFloatとか、そういう型は存在しますが、pythonではvariantなんです。variantというのは適宜変換するということです。
a=20
b= a+14
なんてやっていたら、aは当初intとしてアサインされます。
ところが
a = a*100*100*100*100
なんてやったらfloatに勝手に変換されます。
オーバーフローは発生しません。しかも仮想小数点ですから、2e13なんていう形で扱うことができ、大概の数字が扱えることになります。

しかし、ご存知だと思うんですが、フローティングの数字にすると「桁落ち」がおきます。
2e15+1 なんていう計算は2e15になってしまうのです。全体のうちのわずかな分量は無視されます。
ここは金額計算なんかをするときに気を付けなければいけないので、末尾まで確実な計算をしたいのであれば
x= int(a)+ int(b)
というように、整数形式にして演算する必要があります。
もし円ではなく銭単位にしたいのだったら100倍して計算し、表示するときに100で割ってフローティングにし、fタイプのフォーマットで出力すればいいのです。

このヴァリアントのなかで苦情を言われるのは文字型の数字です。

たとえばファイルに"123”という文字が書いてあって、それを読んでprintすると

'123'
と表示されます。これがmという変数に入れられたとしたとき、
print (m*100)
は可能です。その場合
12300
という表示になります。
print (m)
のときの123は文字列なんです。print (m*100)の結果は数値なんです。


これをあまり意識しないでプログラムすると、途中でエラーが発生します。
だから文字列として扱いたいときはstr(m)と書くのです。数字として扱いたいなら int(m)とするんです。

この話は、比較するときに意識しろということです。
mがintでnがstrなら==で比較するのは意味がありません。
どうしても比較するときには同じ形である必要があるんです。

投稿日時 - 2018-10-04 13:28:33

ANo.2

> pythonはFortran, Cなどの伝統的なプログラム言語とどこか一線を画するようなところがあるのでしょうか。

んー、伝統的かどうか、と言うより「ハードウェアに密接に関係している」か否か、って事でしょうね。Cはノイマン型計算機、要するに今主流のコンピュータですね、での「低レベルで効率的なプログラムを作成する為」設計されていて、効率重視、と言う意味ではFortranもそんなに変わらないでしょう。
Pythonは逆に「プログラミングしやすさ」「ロジック自体を(工学的理由に依らず)追求出来る」ように設計されています。ただ、これも方向性としては「伝統的」で昔からある考え方です。ただ、(Fortranがブイブイ言わせていた)当時と今と何が決定的に違うのか、と言うとハードウェアの処理速度がメチャクチャ速くなった、って事ですね。
Pythonのようなプログラミング言語は「実験室」の中には存在していましたが、市井のマシンは研究室で扱うような「大型コンピュータ」ではないので、それに比べると昔は低速でした。従ってPythonのようなプログラミング言語は「非効率的で非実用的」だった、と言う事です。ところが今はハードウェアが当時の大型コンピュータを凌駕するようなスピードを獲得してるので、「昔は非効率的と思われた」アイディアが、現在だと実用レベルになった、ってそれだけの話ですね。

注: 例えば、Javaは「JVM(Java仮想マシン」上で走るコードを生成するコンパイラ言語で、これがWrite once, Run anywhere(一度(プログラムを)書けば、どこでも実行できる)の理由になっていて、プラットフォーム毎に仮想マシンを用意しておけばどのマシンでもプログラム実行に問題がない環境を実現してる。
が、このアイディア自体は既にそれより20年程前のPascalで実現してて、実際元々Pascalは仮想マシン対象にコンパイルするプログラミング言語だった。つまり「仮想マシン」は別にJavaが「初めて」実現した方法ではない。しかしながら、大型コンピュータ用に元々作られたPascalを「仮想マシン」込みで民生機に持ってくるのはこれがなかなか「資源の無駄遣い」で、8bitパソコン黎明期を除き、仮想マシン対象のPascalは16ビット機以降殆ど使われなくなる(つまり直接機械語/アセンブリ言語に翻訳する処理系が中心となった)。
このように「アイディアが良くても」機械の処理速度が遅いと、民生機上で「実用的に使える」まで時間がかかる、と言うのは良くある事である。

例えば、「超」高級言語としてLispと言うプログラミング言語があります。例えばそのうち、Schemeと言う方言は1975年登場で、と言う事はC言語とほぼ登場時期が同じなんですが(C言語は1972年登場)、こいつも10000!なんかヘーキで計算しますね。

(define (factorial n)
 (let loop ((n n) (acc 1))
  (if (zero? n)
    acc
    (loop (- n 1) (* n acc)))))

(factorial 10000)

結果は桁数がものすごく多くなるんで、ここには載せませんが(笑)、こいつもPythonと同じように「ハードウェア資源が許す限り」どんな大きな数になってもヘーキで計算します。
(例えば https://download.racket-lang.org/ から処理系を入手してみて実験してみて下さい)
これは要するにSchemeの言語仕様で「どんな大きな数になろうと正確な値を返せ」と要求しているから、です(そしてそれを実現する為のプログラミングはメチャクチャ大変ですね・笑。脱帽します)。
そして、伝統的かどうか、と言うより「PythonはCよりLispに近い」発想で設計されている、と言う事です。

> 無理数における数字出現率の傾向を調べるのはこれらの古典的言語でも簡単でしょうか。

任意の無理数さえ獲得すればそんなに難しくないでしょう。あとは、C言語やFortanで扱える「最大の整数」が何か、と言うのさえ把握していれば良いですよね。
つまり、プログラミングは言語によっては「ロジック」ともう一つ「工学上の理由」が要求される場合があって、C言語はその「後者」をケアしないといけない。一方、PythonやLispは後者を「考えなくて良い」ってのが違いで、とは言っても「ロジック」自体は要するに同じですよね。
例えば、前に見せたプログラム

>>> import math
>>> summary = {str(i):0 for i in range(10)}
>>> for key in str(math.factorial(10000)):
   if key in summary:
    summary[key] += 1


>>> summary
{'0': 5803, '1': 3241, '2': 3416, '3': 3258, '4': 3341, '5': 3324, '6': 3324, '7': 3335, '8': 3336, '9': 3282}
>>>

ってのは

1. カウントした「結果」を格納する為の変数summaryを作る(この場合はラベリングがあるのでPythonの辞書型を利用したが、C言語なんかは「長さが10の」配列を用意すれば良い)。
2. このケースの場合は10000!を計算し、結果を文字列に変換してる(C言語の場合でも文字列に変えられれば"数値を文字化した"「配列」を用意した事になる)。
3. 変換した文字列の頭から一文字一文字、「何の数値なのか」確認していって、変数summaryの対応する場所に+1していく。

つまり、「ロジック」自体はPythonでもLispでもC言語でも同じですね。別に変わらない。
ただ、PythonやLispだと「ロジックをそのまま記述すれば」プログラムは完成しますが、C言語なんかの場合だと

1. long型で10000!をそのまま計算出来るのか?計算「させる」為に何か特殊なプログラミングテクニックが必要にならないか?
2. 変数summaryの各格納庫の上限はint型やlong型で間に合うのか?10000!が何桁になるか分からないので、当然各格納庫の「上限」もどうなるか分からない。
3. 10000!をどうやって「配列化」するにせよ、その「配列」の長さがどうなるのかサッパリ分からない。

と言う問題が出てきますよね。ここはぶっちゃけ「ロジック」は全く関係なくって、「C言語を扱う上での工学上の問題」です。C言語だとこういう「工学的な壁」がアチコチ立ちはだかるので、結果C言語プログラミングはメンド臭くて厄介になるわけですね。

写真: 同じくLisp系言語Common Lispでの10000!の各数字(0~9)の出現率を調べるプログラム。
0が5803個、1が3241個ある、とPythonでの解と一致している。

投稿日時 - 2018-09-27 00:34:49

ANo.1

> コンピュータで使える数字の桁数って限定されると思っていたのですが、pythonだと計算できるのでしょうか。

はい、出来ます。
C言語のような低レベルな言語だと限定されてるんですが、仕組みとしてはメモリのひと塊(8bit、16bit、32bit、64bit等)で扱える数が上限ですが、Python(や同じくC等より高級な言語)の場合、必要になったらもっとメモリの塊を追加出来るように設計してるので、必要な分だけの数が計算出来ます。
ただし、本当の上限は、要するに貴方が持ってるPCがメモリをどの程度搭載してるかに左右されます。が、それを全部使い切るのは貴方が想像してる以上のデカさの数でしょうから、気にしなくて結構です。

> また、ものすごく桁数が長い数字とか無理数とかですが、数字(0-9)が表れる頻度を調べることができないかと思いますが、プログラムを組むことは可能でしょうか。円周率でも平方根、ネイピア数とかでもですが。

出来るんじゃないですか?
その「数」の計算方法は任せますが、例としては次のように出来ますね。

>>> import math
>>> summary = {str(i):0 for i in range(10)}
>>> for key in str(math.factorial(10000)):
   if key in summary:
    summary[key] += 1


>>> summary
{'0': 5803, '1': 3241, '2': 3416, '3': 3258, '4': 3341, '5': 3324, '6': 3324, '7': 3335, '8': 3336, '9': 3282}
>>>

投稿日時 - 2018-09-25 14:11:50

お礼

回答ありがとうございました。pythonはFortran, Cなどの伝統的なプログラム言語とどこか一線を画するようなところがあるのでしょうか。無理数における数字出現率の傾向を調べるのはこれらの古典的言語でも簡単でしょうか。こういう分野をやったことがないのでコード化を思いつかなったのですが。

投稿日時 - 2018-09-26 20:55:08

あなたにオススメの質問