概要
Twitterを眺めていたら、「Sum of three cubes for 42 finally solved -- using real life planetary computer」という記事が引用されていました。
42といえば、なんとなく気になる数字ですよね。
使われている数字が大きいので、JavaScriptのBigIntの出番です。
BigIntとは
BigIntは、JavaScriptで任意の大きな整数を扱うことができるライブラリです。
(JavaScriptの標準の数値型であるNumberで信頼できる値として扱えるのは( 2 ^ 53 - 1)まで)
このエントリを書いている時点で、TC39(Ecma International, Technical Committee 39)でStage3です。
JavaScriptエンジンのV8では実装されたものがあり、Node V10 LTS以降で利用可能になっています。
コード
GitHubに置いてあります。https://github.com/hrkt/answer42/releases/tag/1.0.0
BigIntを使うときは、BigInt(数値)としてオブジェクトを初期化するほかに、末尾に「n」をつけてBigIntであることを示すことができます。
// using BigInt(https://developer.mozilla.org/ja/docs/Web/JavaScript
exports.answer42 = function() {
const x = -80538738812075974n
const y = 80435758145817515n
const z = 12602123297335631n
return x ** 3n + y ** 3n + z ** 3n
}
補足: 3乗を示している「**3n」 には、BigIntであることを示す「n」が必要です。BigIntとNumberの演算は、暗黙の裡に型変換されることがないため、下記のようなエラーとなります。。
return x ** 3 + y ** 3 + z ** 3
^
TypeError: Cannot mix BigInt and other types, use explicit conversions
テストしてみます。テストコードは下記です。
const assert = require('assert')
const answer = require("../answer")
describe('The answer', function() {
describe('#answer42()', function() {
it('should return 42', function() {
assert.equal(answer.answer42(), 42n)
})
})
})
実行してみます。
PS C:\Users\work\answer42\js> npm run test
> answer42@1.0.0 test C:\Users\work\answer42\js
> mocha
The answer
#answer42()
√ should return 42
1 passing (232ms)
答えがあっています。
テストは、これをGitHub Actionsで動かしておきます。設定は以下の通り。
name: Node CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 12.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: npm install, build, and test
run: |
(cd js; npm install)
(cd js; npm run test)
env:
CI: true
補足:Numberで計算した場合との違い
BigIntを使わず、下記のようにNumberで実行した場合と比べてみます。
// using Number(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number)
exports.answer42InNumber = function() {
const x = -80538738812075974
const y = 80435758145817515
const z = 12602123297335631
return x ** 3 + y ** 3 + z ** 3
}
main.jsはこんな感じです。
const answer = require('./answer')
console.log("BigInt:" + answer.answer42())
console.log("Number:" + answer.answer42InNumber())
実行してみます。
PS C:\Users\work\answer42\js> npm run start
> answer42@1.0.0 start C:\Users\work\answer42\js
> node main.js
BigInt:42
Number:1.989298733926156e+35
Numberだと、生命、宇宙、そして万物についての究極の疑問の答え42とならないことがわかります。
まとめ
JavaScriptのBigIntを使い、大きな数を扱うときの例を扱いました。
補足
Wolfram Alpha様であれば実は一瞬で解けたりするのか、と思ってためしてみましたが、そんなことはありませんでした。
https://www.wolframalpha.com/input/?i=x+%5E+3+%2B+y+%5E+3+%2B+z+%5E+3+%3D+42
冒頭に引用した記事によると、分散計算用のツール"Charity Engine"を使って50万台以上のPCの「空き時間」で計算したとのこと。