exp(pi * sqrt(n))が整数に近くなるnを探す
Posted feedbacks - Nested
Flatten Hidden1から199まで(Range)の整数から、選ぶ(Select)、 基準は、 「x=Exp[Pi Sqrt[n]]として(With)、Abs[x-Round@x]が10^-4以下のもの」 「10^-4を0.0001と書いてはいけない」という問題かな
1 2 3 4 | Select[Range@199,
Function[{n},
With[{x = Exp[Pi Sqrt@n]},
Abs[x - Round@x] <= 10^-4]]]
|
>「10^-4を0.0001と書いてはいけない」 そうなんですか?
もともとかなり古いライブラリだけど。。。使い勝手は悪くないと思う。 CReal モジュールを使う このモジュールではデフォルトで小数点以下40桁まで表示。
see: CReal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | module Main (main) where
import CReal
default (CReal)
f :: CReal -> (Int, CReal)
f n = (ceiling n, exp $ pi * sqrt n)
g :: CReal -> Bool
g n = 0.0001 > if 0.5 > n' - n then n' - n else 1 - (n' - n)
where n' = fromInteger $ ceiling n
main :: IO ()
main = mapM_ (putStrLn . show) $ filter (g . snd) $ map f $ [1..200]
{-
*Main> :main
(37,199148647.9999780465518567665009238753359004336659)
(58,24591257751.9999998222132414695761923552658122276102)
(67,147197952743.9999986624542245068292613125786285081833)
(163,262537412640768743.9999999999992500725971981856888793538563)
-}
|
あっ。200未満か。まぁいいか。
f の定義がちょっとアレげなので、リファクタリング f :: Integer -> (Integer, CReal) f n = (n, exp $ pi * sqrt (fromInteger n))
20000以下で調べてみたら, (37,199148647.9999780465518567665009238753359004336659) (58,24591257751.9999998222132414695761923552658122276102) (67,147197952743.9999986624542245068292613125786285081833) (163,262537412640768743.9999999999992500725971981856888793538563) (232,604729957825300084759.9999921715268564302785946808125512858845) (652,68925893036109279891085639286943768.0000000001637386442092346075723290625709) (719,3842614373539548891490294277805829192.9999872495660121875632701836570684449713) (1169,44555719382988281777368496770130045948309444044.9999608028638684615024311524958053653676) (1467,18095625621654510801615355531263454706630064771074975.9999999901236936712413276522472419790897) (2608,4750778730825177725463920948909726618214491718039471366318747406368792.0000003084643221299811801879962000157848) (4075,1247257156019637304856107520018074552566824585862995272173368815794085495792299621093743.9999936541874689715769690801805608661462) (5773,46309587632860353087565367053742331250287153098248715578209888177688338779879045292937243508078581367989.9999155688538141662485158867634827239495) (5868,327451666639079200503292535866541250265248788274691526825971156747731856100971255480468836963064283775072.0000971752541625920841201776565931010652) (14370,35853082260707987565058966569844138230073788113687426211111838655289387060751425253235617998546754095483027646820100689677619375065141669969772221012766669726610356.0000809961676073067730760452585504610564) (19183,932865712335864748985892137487224407024879778525651187858506512365813448395651706659915858370562540302669863675912417090372354324023009941419688829317421179304232837551154837099435561184585.9999366484683843100572967742009198686654) という結果でした.163が題意の意味で一番整数に近いようです. 所要時間は,time ではかって, 2128.54s user 8.40s system 96% cpu 36:49.42 total でした.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | module Main (main) where
import Data.List
import System.Environment
import System.IO
import CReal
default (CReal)
f :: CReal -> (Int, CReal)
f n = (ceiling n, exp $ pi * sqrt n)
g :: CReal -> Bool
g n = 0.0001 > h n
h :: CReal -> CReal
h n = if 0.5 > n' - n then n' - n else 1 - (n' - n)
where n' = fromInteger $ ceiling n
main :: IO ()
main = do { args <- getArgs
; case args of
[] -> loop 100 0
n:_ -> loop (read n) 0
}
loop n m
| n == m = return ()
| True = mapM_ (k . f) [(m*100)+1 .. (m+1)*100] >> loop n (m+1)
k :: (Int,CReal) -> IO ()
k n@(i,r) = if g r then hPutStrLn stdout (show n) >> hFlush stdout
else putStr (show i ++ "\r") >> hFlush stdout
|
cpuスペックを書きわすれた。 Intel(R) Pentium(R) M processor 2.13GHz です
ええと、long doubleは18桁くらいの精度ですよね。 67の計算には足りて、163の計算には足りないのでしょうね、きっと。
Apfloatを使いました。
それだけじゃ何も芸がない感じだったので、implicitでApfloatをあまり意識せずに書ける様にしました。
see: Apfloat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import org.apfloat._
import scala.runtime._
object findAlmostInt {
implicit def toApfloat[A <: AnyVal](i:A):Apfloat = {
new Apfloat(i.toString, 32)
}
implicit def toDouble(i:Apfloat):RichDouble = i.doubleValue.toDouble
val PI = ApfloatMath.pi(32)
def apply(i:int, j:int) = {
(i to j-1).filter{n =>
val x = ApfloatMath.exp(PI.multiply(ApfloatMath.sqrt(n)))
val y = x.ceil.subtract(x)
(if(y<0.5){y}else{1.subtract(y)}).abs < 0.0001
}.toList
}
}
println(findAlmostInt(1,200))
|
便乗。このライブラリは重宝しそう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function doukaku60(){ // 精度的に 58 が限界。
with(Math) for(var r = [], f, n = 1; n < 200; n++)
if(abs((f = exp(PI * sqrt(n))) - round(f)) < 1e-4) r.push([n, f]);
return r;
}
// ↓ Apfloat を用いて書き換え //
function doukaku60_(){
var F = Packages.org.apfloat.Apfloat;
var M = Packages.org.apfloat.ApfloatMath;
for(var r = [], p = 25, h = new F(0.5, p), f, n = 1; n < 200; n++){
f = M.exp(M.pi(p).multiply(M.sqrt(new F(n, p))));
if(M.abs(f.subtract(f.add(h).floor())).floatValue() < 1e-4) r.push([n, f.toString(true)]);
}
return r;
}
print(doukaku60_().join('\n'))
|
Decimalを使って多倍長計算。sqrtは組み込みのものを使い、expは安直にマクローリン展開で収束するまで回しています。psycoを使ってますが、それでもかなり遅いです。
see: Pythonで10進数計算したり数値を丸めたりする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | from decimal import *
import itertools
def decimal_exp(x):
curr, bunsi, bunbo = Decimal("1.0"), Decimal("1.0"), 1
for k in itertools.count(1):
bunsi *= x
bunbo *= k
next = curr + bunsi / bunbo
if curr == next:
return curr
curr = next
def main():
pi = Decimal("3.141592653589793238462643383279")
for n in xrange(1, 200):
x = decimal_exp(pi * Decimal(n).sqrt()) # result
i = x.quantize(Decimal("1.0"), rounding=ROUND_HALF_UP) # nearest integer
if (x - i).quantize(Decimal("0.00001"), rounding=ROUND_DOWN) == 0:
print n, x
if __name__ == '__main__':
try:
import psyco
except ImportError:
pass
else:
psyco.full()
main()
|
あああ、すみません。組み終わった後に ".00..." という表記を自分好みの "0.00.." という表記に書き換えたときにエンバグしちゃったみたいで、37 が表示されなくなってました。プラス評価もらったのに申し訳ない。
1 2 3 4 5 6 7 8 9 10 11 | --- a.orig Sat Sep 15 15:12:33 2007
+++ a.py Sat Sep 15 15:12:50 2007
@@ -16,7 +16,7 @@
for n in xrange(1, 200):
x = decimal_exp(pi * Decimal(n).sqrt()) # result
i = x.quantize(Decimal("1.0"), rounding=ROUND_HALF_UP) # nearest integer
- if (x - i).quantize(Decimal("0.00001"), rounding=ROUND_DOWN) == 0:
+ if (x - i).quantize(Decimal("0.0001"), rounding=ROUND_DOWN) == 0:
print n, x
if __name__ == '__main__':
|
人のふんどしで相撲を取るシリーズ。 上記のコードを任意精度演算ライブラリとしてgmpを使いCに移植しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmp.h>
mpf_t curr, next, bunsi, bunbo;
void mpf_exp(mpf_t x)
{
unsigned int k;
mpf_set_ui(curr, 1.0);
mpf_set_ui(bunsi, 1.0);
mpf_set_ui(bunbo, 1.0);
for (k = 1; ;k++) {
mpf_mul(bunsi, bunsi, x);
mpf_mul_ui(bunbo, bunbo, k);
mpf_div(next, bunsi, bunbo);
mpf_add(next, next, curr);
if (mpf_cmp(curr, next) == 0) {
mpf_set(x, curr);
return;
}
mpf_set(curr, next);
}
}
int main(int argc, char *argv[])
{
char buf[128], tmp[8];
double d;
int i;
mpf_t x, pi;
mpf_set_default_prec(128);
mpf_init(curr);
mpf_init(next);
mpf_init(bunsi);
mpf_init(bunbo);
mpf_init(x);
mpf_init(pi);
mpf_set_str(pi, "3.1415926535897932384626433832795029", 10);
for (i = 1; i < 200; i++) {
mpf_set_ui(x, i);
mpf_sqrt(x, x);
mpf_mul(x, x, pi);
mpf_exp(x);
gmp_sprintf(buf, "%.*Ff", 6, x);
d = atof(strncpy(tmp, strchr(buf, '.'), 7));
if ((d < 0.0001) || ((1 - d) < 0.0001))
printf("%d %s\n", i, buf);
}
mpf_clear(curr);
mpf_clear(next);
mpf_clear(bunsi);
mpf_clear(bunbo);
mpf_clear(x);
mpf_clear(pi);
return 0;
}
|
上記のコードをctypesを使ってpythonで実装してみました。 Cのヘッダファイル内で#ifdefやら#defineが多用されているので、もしかしたら 環境依存になっているかもしれません。 呼び出し毎にインスタンスを生成するexp関数と、静的なインスタンスを持つ exp2関数の速度を比較すると、exp2の方が20%ほどはやいようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | from ctypes import *
gmp = cdll.LoadLibrary('libgmp.so')
for s in ['set_default_prec', 'init', 'clear', 'set', 'set_ui', 'set_str', 'mul'
, 'mul_ui', 'div', 'add', 'cmp', 'sqrt', 'printf', 'snprintf']:
try:
gmp.__setattr__(s, gmp.__getattr__('__gmpf_'+s))
except:
gmp.__setattr__(s, gmp.__getattr__('__gmp_'+s))
def mpf():
class mpf_t(Structure):
_fields_ = [("_mp_prec", c_int), ("_mp_size", c_int), ("_mp_exp", c_int), ("
_mp_d", c_void_p)]
def __init__(self):
Structure.__init__(self)
gmp.init(byref(self))
def __del__(self):
try:
gmp.clear(byref(self))
except:
pass
return pointer(mpf_t())
def exp(rop, op):
curr, bunsi, bunbo, next = mpf(), mpf(), mpf(), mpf()
gmp.set_ui(curr, c_ulong(1))
gmp.set_ui(bunsi, c_ulong(1))
gmp.set_ui(bunbo, c_ulong(1))
k = 1
while True:
gmp.mul(bunsi, bunsi, op)
gmp.mul_ui(bunbo, bunbo, c_ulong(k))
gmp.div(next, bunsi, bunbo)
gmp.add(next, next, curr)
if gmp.cmp(curr, next) == 0:
gmp.set(rop, curr)
return
gmp.set(curr, next)
k += 1
def exp2(rop, op):
try:
curr, bunsi, bunbo, next = exp2.curr, exp2.bunsi, exp2.bunbo, exp2.next
except:
exp2.curr, exp2.bunsi, exp2.bunbo, exp2.next = mpf(), mpf(), mpf(), mpf()
curr, bunsi, bunbo, next = exp2.curr, exp2.bunsi, exp2.bunbo, exp2.next
gmp.set_ui(curr, c_ulong(1))
gmp.set_ui(bunsi, c_ulong(1))
gmp.set_ui(bunbo, c_ulong(1))
k = 1
while True:
gmp.mul(bunsi, bunsi, op)
gmp.mul_ui(bunbo, bunbo, c_ulong(k))
gmp.div(next, bunsi, bunbo)
gmp.add(next, next, curr)
if gmp.cmp(curr, next) == 0:
gmp.set(rop, curr)
return
gmp.set(curr, next)
k += 1
def frac_f(x):
buf = c_buffer(256)
gmp.snprintf(buf, len(buf)-1, "%.Ff", x)
return float(buf.value[buf.value.index('.'):])
def main():
from time import time
t = time()
gmp.set_default_prec(128)
x, pi = mpf(), mpf()
gmp.set_str(pi, "3.141592653589793238462643383279502884197", 10)
for i in range(1, 200):
gmp.set_ui(x, c_ulong(i))
gmp.sqrt(x, x)
gmp.mul(x, x, pi)
# exp(x, x)
exp2(x, x)
f = frac_f(x)
if f < 0.0001 or (1-f) < 0.0001:
gmp.printf("%d %.6Ff\n", i, x)
print time() - t
if __name__ == '__main__':
main()
|
#3130の匿名さんのアイデアを拝借して組みなおしてみたところ、2.6倍早くなりました。
see: これを組んでいて、Python2.5のバグを踏んでしまった(汗)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | from decimal import *
import itertools
def divmod(x, y): # workaround for python2.5 decimal mod bug.
div = x // y
mod = x - div * y
return div, mod
def decimal_exp(x):
div, mod = divmod(x, Decimal("2.3025850929940456840179914546844"))
curr, bunsi, bunbo = Decimal("1.0"), Decimal("1.0"), 1
for k in itertools.count(1):
bunsi *= mod
bunbo *= k
next = curr + bunsi / bunbo
if curr == next:
return (10 ** div) * curr
curr = next
def main():
pi = Decimal("3.141592653589793238462643383279")
for n in xrange(1, 200):
x = decimal_exp(pi * Decimal(n).sqrt()) # result
i = x.quantize(Decimal("1.0"), rounding=ROUND_HALF_UP) # nearest integer
if (x - i).quantize(Decimal("0.0001"), rounding=ROUND_DOWN) == 0:
print n, x
if __name__ == '__main__':
try:
import psyco
except ImportError:
pass
else:
psyco.full()
main()
|
このバグはPython2.5.2で修正されました。というわけでパッチです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | --- a.py Thu Feb 28 02:32:31 2008
+++ a.py Thu Feb 28 02:32:46 2008
@@ -1,10 +1,11 @@
from decimal import *
-import itertools
+import itertools, sys
-def divmod(x, y): # workaround for python2.5 decimal mod bug.
- div = x // y
- mod = x - div * y
- return div, mod
+if sys.version_info[:3] <= (2, 5, 1):
+ def divmod(x, y): # workaround for python2.5 decimal mod bug.
+ div = x // y
+ mod = x - div * y
+ return div, mod
def decimal_exp(x):
div, mod = divmod(x, Decimal("2.3025850929940456840179914546844"))
|
素直にbigdecimalを使って。
see: Rubyリファレンスマニュアル - bigdecimal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | require "bigdecimal"
require "bigdecimal/math.rb"
include BigMath
def exp_pi_sqrt(x)
tmp = exp(PI(30) * sqrt(BigDecimal(x.to_s), 30), 30)
err = BigDecimal 0.0001.to_s
if (tmp > (tmp.floor - err)) and (tmp < (tmp.floor + err))
print "#{x} #{tmp.to_s("F")}\n"
elsif (tmp > (tmp.ceil - err)) and (tmp < (tmp.ceil + err))
print "#{x} #{tmp.to_s("F")}\n"
end
end
=begin
for i in 1 .. 200
exp_pi_sqrt i
end
=end
|
強引に解いてみました。BigDecimalを使用していますがライブラリがないので自前で計算しています。expの計算方法が非効率的なので非常に時間がかかります(苦笑)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | import java.math.BigDecimal;
import java.math.MathContext;
public class Sample {
private static final double EPS = 0.000001;
private static final double EPS2 = 1e-25;
private static final BigDecimal TWO = new BigDecimal(2);
private static final BigDecimal PI = new BigDecimal
("3.1415926535897932384626433");
public static BigDecimal exp(BigDecimal x) {
int i = 1;
BigDecimal b = new BigDecimal(i++);
BigDecimal c = x;
BigDecimal a = new BigDecimal(1.0);
BigDecimal d;
while ((d = c.divide(b, MathContext.DECIMAL128)).doubleValue() > EPS) {
a = a.add(d);
b = b.multiply(new BigDecimal(i++));
c = c.multiply(x);
}
return a;
}
public static BigDecimal sqrt(BigDecimal a) {
BigDecimal x = new BigDecimal(10);
BigDecimal delta;
do {
delta = (x.multiply(x).subtract(a)).divide(TWO.multiply(x),
MathContext.DECIMAL128);
x = x.subtract(delta);
} while (delta.abs().doubleValue() > EPS2);
return x;
}
public static void main(String[] args) {
for (int i = 1; i < 200; i++) {
BigDecimal er = exp(PI.multiply(sqrt(new BigDecimal(i))));
BigDecimal rer = new BigDecimal(er.toBigInteger());
double a = er.subtract(rer).doubleValue();
if (a < 0.0001 || a > 0.9999) {
System.out.printf("%d: %f%n", i, er);
}
}
}
}
|
expの計算の効率を上げる事で、なんとか実用(?)的な性能にする事ができました。
x = n log 10 + k の時
exp(x) = 10^n * exp(k)
の性質を使っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import java.math.BigDecimal;
import java.math.MathContext;
public class Sample {
private static final double EPS = 0.000001;
private static final double EPS2 = 1e-25;
private static final BigDecimal TWO = new BigDecimal(2);
private static final BigDecimal PI = new BigDecimal
("3.141592653589793238462643383279");
private static final BigDecimal LN10 = new BigDecimal
("2.3025850929940456840179914546844");
public static BigDecimal exp(BigDecimal x) {
BigDecimal[] dr = x.divideAndRemainder(LN10);
int n = dr[0].intValue();
x = dr[1];
int i = 1;
BigDecimal b = new BigDecimal(i++);
BigDecimal c = x.scaleByPowerOfTen(n);;
BigDecimal a = new BigDecimal(1.0).scaleByPowerOfTen(n);
BigDecimal d;
while ((d = c.divide(b, MathContext.DECIMAL128)).doubleValue() > EPS) {
a = a.add(d);
b = b.multiply(new BigDecimal(i++));
c = c.multiply(x);
}
return a;
}
public static BigDecimal sqrt(BigDecimal a) {
BigDecimal x = new BigDecimal(Math.sqrt(a.doubleValue()));
BigDecimal delta;
do {
delta = (x.multiply(x).subtract(a)).divide(TWO.multiply(x),
MathContext.DECIMAL128);
x = x.subtract(delta);
} while (delta.abs().doubleValue() > EPS2);
return x;
}
public static void main(String[] args) {
for (int i = 1; i < 200; i++) {
BigDecimal er = exp(PI.multiply(sqrt(new BigDecimal(i))));
BigDecimal rer = new BigDecimal(er.toBigInteger());
double a = er.subtract(rer).doubleValue();
if (a < 0.0001 || a > 0.9999) {
System.out.printf("%d: %f%n", i, er);
}
}
}
}
|





herumi
#3416()
Rating0/2=0.00
Pythonで34行のスクリプトを書いて得られた出力の例が下のようになります。
この問題は光成さんに教えて頂いた e^{π*sqrt{163}}≒26253741640768744 が元になっています。ご協力ありがとうございました。[ reply ]