B - Geometric Sequence Editorial
by
rsk0315
誤差で WA になるケースの構築に関して
分数を誤差なく比較する必要があります。 (公式解説 からの引用)
こうした話はしばしば目にしますが、実際に誤差が出るケースを見ないと「なんか誤差というものがあるらしい?」となってしまう人も多いでしょう。
次の単純な解法を考えます。
use proconio::input;
fn main() {
input! {
a: [f64],
}
let res = a.windows(2).all(|w| w[1] / w[0] == a[1] / a[0]);
println!("{}", if res { "Yes" } else { "No" });
}
Yes/No の問題なので、次の 2 通りが考えられます。
- 実際には Yes だが No を出力してしまう。
- 実際には No だが Yes を出力してしまう。
まず、今回の問題においては前者のケースは存在しないことを示します。
正解が Yes であるとき、ある \(r\) が存在して、任意の \(i\) に対して \(\frac{a_{i+1}}{a_i} = r\) が成り立ちます。
\(10^9\) 以下の整数は誤差なく表せるため、任意の \(i\) に対して \(a_{i+1}\oslash a_i = \hat r\) となります。ここで、\(\hat r\) は \(r\) を f64
で表せるように丸めた値、\(x\oslash y\) は \(\frac xy\) を f64
で表せるように丸めた値とします。
以降、正解が No であるのに Yes と判定してしまうケースを考えます。\(\frac{a_{i+1}}{a_i} \ne \frac{a_2}{a_1}\) となる \(i\) が一つでも存在すれば十分なので、\(N=3\) としてよいです。
ある(これから考える)\(\varepsilon\) に対して \(\frac{a_2}{a_1} = \frac{a_3}{a_2} + \varepsilon\) とします。\(a_2^2=a_1a_3+a_1a_2\cdot\varepsilon\) です。\(a_1a_2\cdot\varepsilon=1\) としてみると、\((a_2-1)(a_2+1) = a_1a_3\) とできます。
ここで、\((a_1, a_3) = (a_2-1, a_2+1)\) としてみましょう。これにより、探索するべき変数が \(a_2\) 一つになったため、\(10^9\) 個程度であればすぐに調べられます。
実際、たとえば \(a_2 = 67114657\) とすると下記が成り立ちます。
\[ \begin{aligned} a_2\oslash a_1 &= a_3\oslash a_2 \\ &=1.000000014899875{\small 111495930468663}{\scriptsize 57326507568359375} \\ &= \texttt{0x10000003FFE960}\cdot 2^{-52}. \end{aligned} \]
なので、下記のケースによって WA にすることができます。
3
67114656 67114657 67114658
なお、たとえば \(a_2 = 999999807\) のケースでは、下記のように No と判定されます。
\[ \begin{aligned} a_2\oslash a_1 &= 1.000000001000000{\small 304784975924121}{\scriptsize 6816008090972900390625} \\ &\ne 1.000000001000000{\small 082740370999090}{\scriptsize 373516082763671875} \\ &= a_3\oslash a_2. \end{aligned} \]
今回は(たまたま)\(a_2 = 10^9-1\) のケースで WA にすることもできますが、誤差は容易に予測できるものではないので、「大きいケースを適当に入れてみてうまくいったから、たぶん整数型を使わなくても大丈夫であろう」のような判断はしないようにしましょう。
posted:
last update: