JavaScriptに関するお知らせ

SINCE2019
>
【JS】Pontaの実質還元率をJavaScriptで求める

【JS】Pontaの実質還元率をJavaScriptで求める



こんにちは!今日はJavaScriptを用いたアルゴリズムについて身近なPontaを例に書きます。

Ponta

Ponta

Pontaとはローソンで出すと100円で1ポイント貰えるあれです。 説明不要ですね。

代金からもらえるPontaを求める関数はこんな感じ。

const getPonta = price =>
  price / 100 | 0

console.log(getPonta(100))// 1
console.log(getPonta(200))// 2

|0は小数点以下切り捨てです。

ローソンで1億円買ったら

Pontaは還元率1%。1億買ったらPontaは1,000,000円貰えるのでしょうか。

試してみよう

console.log(getPonta(100000000))// 1000000

貰えました。しかし...

Ponta端数問題

Pontaは端数切り捨てです。100円でも199円でも同じ。

console.log(getPonta(100))// 1
console.log(getPonta(199))// 1

つまり何が言いたいのか

Pontaを例にしましたが、準通貨の還元率は往々にして切り捨て方式だったりします。

クレジットカードで「還元率○○%!」ってあるやつは 実際もっと低かったりすることもある、ということです:(

クレジットカードを選ぶ時はその辺にも気をつけるといいですよ!!

Pontaの還元率を求める

え?終わりじゃないの?

もう言いたいことは終わりましたが、技術ブログなのでここからがおまけ本題です。

じゃあ実際にローソンで1億円分買って検証してみましょう。JavaScriptで。

前提条件

  • ローソンで1億円分買う。
  • 1億円に到達するまで買い続ける。
  • Pontaは100円で1ポイント貯まるとする。
  • 1回の買い物は0円(トイレ借りるだけ)〜1,000円(大人がコンビニで躊躇せずに使える)とする。
  • 1億42円とか最後がオーバーするのはok。

やっていきましょう。

アルゴリズム

いくつかの関数を考えてみます。

さっき作ったgetPonta

const getPonta = price =>
  price / 100 | 0

次に0〜1,000をランダムで返す関数。

const getPayment = () =>
  Math.random() * 1000 | 0

配列の合計を求める関数。

const sum = payments =>
  payments.reduce((total, payment) =>
    total + payment
  )

1億円まで買い続ける関数shop at lawson。

延々と再帰を行い買い物を続け、毎回支払った金額の配列を [183, 744, 938...]みたいな感じで作ります。

const shopAtLawson = payments =>
  sum(payments) >= 100000000
  ? payments
  : shopAtLawson([... payments, getPayment()])

↑の支払った金額から合計Pontaを求める関数。

const sumTotalPontas = totalPayments =>
  totalPayments.reduce((total, payment) =>
    total + getPonta(payment)
  ,0)

最後に、還元率を求める式。

const rate = totalPoints / 1000000 * 100

これらの関数を用いて、答えを求めていきます。

答え(注: 糞重い)

注: 糞重い

const getPonta = price =>
  price / 100 | 0

const getPayment = () =>
  Math.random() * 1000 | 0

const sum = payments =>
  payments.reduce((total, payment) =>
    total + payment
  )

const shopAtLawson = payments =>
  sum(payments) >= 100000000
  ? payments
  : shopAtLawson([... payments, getPayment()])

const totalPayments = shopAtLawson([0])

const sumTotalPontas = totalPayments =>
  totalPayments.reduce((total, payment) =>
    total + getPonta(payment)
  ,0)

const totalPoints = sumTotalPontas(totalPayments)

const rate = totalPoints / 100000000 * 100

console.log(rate)// 0.9010

今回のケースでは還元率は1%ではなく0.9%に近似されました。

数学的な答え(多分)

実質還元率

= 還元率 -

(((端数の最小値 + 最大値) / 2 )

/ ((支払い額の最小値 + 最大値) / 2))

= 1 - (((0 + 100) / 2) / ((0 + 1000) / 2))

= 0.9

結論

  • ポイントの切り捨てに注意しよう。特に少額の支払いをちょいちょいするやつ。
  • 再帰関数は糞重い

今日はいい記事が書けた!ハッピー<3<3<3



PREV
2019-05-04
【JS】配列検索はsome?every?find?indexOf?includes?test?exec?match?search?

NEXT
2019-05-08
【JS】JavaScriptを書いてる人が思うPythonの若干嫌なところ