[解決済み] 3色の三角形
質問
この問題に対するコードを作ろうとしています。
(出典 https://www.codewars.com/kata/insane-coloured-triangles/train/c )
色の三角形は、それぞれの色の列から作成されます。 赤、緑、青のいずれかになります。連続した行は、それぞれ1つ少ない色で構成されています。 最後に触れた2色を考慮して生成されます。 を、前の行の これらの色が同じであれば、同じ色 が新しい行に使われます。異なる場合は、欠落した色が新しい行に使用されます。 新しい行で使用されます。これを、最後の行が完成するまで続けます。 が1色で生成される。
例えば、異なる可能性としては
Colour here: G G B G R G B R Becomes colour here: G R B G With a bigger example: R R G B R G B B R B R G B R B G G B R G G G R G B G B B R R B G R R B G
三角形の最初の行が文字列として与えられ、下の行に表示される最終的な色を文字列として返すのがあなたの仕事である。上の例では、次のように与えられる。
'RRGBRGBB'
を返さなければなりません。'G'
.制約事項です。
1 <= length(row) <= 10 ** 5
入力文字列には、大文字の '
B', 'G' or 'R'
.正確なテストケースの数は以下のようになります。
100 tests of 100 <= length(row) <= 1000 100 tests of 1000 <= length(row) <= 10000 100 tests of 10000 <= length(row) <= 100000
例
triangle('B') == 'B' triangle('GB') == 'R' triangle('RRR') == 'R' triangle('RGBG') == 'B' triangle('RBRGBRB') == 'G' triangle('RBRGBRBGGRRRBGBBBGG') == 'G'
このコードを作成し、すべてのサンプルテイストで動作するようになりましたが
length of row > 25
の長さになる場合があり、失敗します。
100,000
この問題を解決するために何か提案があれば、少なくとも問題を解決できる数学的な公式や小さなヒントがあれば教えてください。
ちなみにこのコードは、このサイトで問題を解決する数学的な方法を見つけた後に作りました。
https://math.stackexchange.com/questions/2257158/three-color-triangle-challenge
long long factorial(long long num)
{
long long fact = num;
if (fact == 0)
fact = 1;
while (num > 1)
{
num--;
fact *= num;
}
return (fact);
}
long long combination(long long n, long long k, long long fact_n)
{
long long fact_k = factorial(k);
long long comb = ( fact_n / (fact_k * factorial(n - k)) );
return (comb);
}
char triangle(const char *row)
{
long long len = strlen(row);
long long n = len - 1;
long long k = 0;
int sign = pow(-1,n);
long long sum = 0;
char *result = "RGB";
int a;
long long fact_n = factorial(n);
while (k < len) //This while calculate Segma (∑).
{
if (row[k] == 'R')
a = 0;
else if (row[k] == 'G')
a = 1;
else if (row[k] == 'B')
a = 2;
if (a != 0)
sum = sum + (combination(n,k,fact_n) * a);
k++;
}
sum = sign * (sum % 3);
if (sum == -1 || sum == -2)
sum += 3;
return (result[sum]);
}
解決方法は?
ご提示いただいたリンク先の計算式が正しいものとします。
整数のオーバーフローを回避するために、以下を適用する必要があります。 モジュロ演算規則 :
(a * b) mod c = ((a mod c) * (b mod c)) mod c
(a ± b) mod c = ((a mod c) ± (b mod c)) mod c
数式に適用する。
しかし、どのように計算するのでしょうか?
... 係数そのものを直接計算することなく(オーバーフローを引き起こす可能性がある)?
3は素数なので、これを実現するためには ルーカスの定理 :
... ここで
n_i, m_i
は
i
-の第
n, m
で
ベース3
.
Base-3への変換は、整数の割り算で簡単にできます。
// convert a number to base 3
// and returns the number of digits
unsigned conv_base_3(unsigned n, unsigned max, unsigned* out)
{
unsigned i = 0;
while (i < max && n > 0)
{
out[i] = n % 3;
n /= 3;
i++;
}
return i;
}
ただし
n_i, m_i
は常に範囲内の
[0, 2]
(基本3桁なので)。
C(n_i, m_i)
は非常に計算しやすい。
// calculate the binomial coefficient for n < 3
unsigned binom_max_2(unsigned n, unsigned k)
{
if (n < k)
return 0;
switch (n)
{
case 0:
case 1:
return 1;
case 2:
return 1 + (k == 1);
// shouldn't happen
default:
return 0;
}
}
そして、今度は定理そのものです。
// Lucas's theorem for p = 3
unsigned lucas_3(
unsigned len_n, const unsigned * dig_n,
unsigned len_k, const unsigned * dig_k
)
{
// use modulo product rule:
// prod[i] % 3 = ((prod[i - 1] % 3) * value[i])
unsigned prod = 1;
for (unsigned i = 0; i < len_n; i++) {
unsigned n_i = dig_n[i];
unsigned k_i = (i < len_k) ? dig_k[i] : 0;
prod = (prod * binom_max_2(n_i, k_i)) % 3;
}
return prod % 3;
}
文字変換を行います。
// convert from 012 to RGB
char int_2_char(int i)
{
switch (i) {
case 0: return 'R';
case 1: return 'G';
case 2: return 'B';
// shouldn't happen
default:
return '\0';
}
}
// convert from RGB to 012
unsigned char_2_int(char c)
{
switch (c) {
case 'R': return 0;
case 'G': return 1;
case 'B': return 2;
// shouldn't happen
default:
return 3;
}
}
最後に、三角形のアルゴリズムです。
// the problem constraints state that n <= 10 ** 5
// max number of base-3 digits
#define MAX_N_LOG_3 11
// main algorithm function
char triangle(const char * input)
{
unsigned sum = 0;
const int n = strlen(input);
// calculate digits of n - 1
unsigned dig_n[MAX_N_LOG_3];
unsigned len_n = conv_base_3(n - 1, MAX_N_LOG_3, dig_n);
for (unsigned km1 = 0; km1 < n; km1++)
{
// calculate digits of k - 1
unsigned dig_k[MAX_N_LOG_3];
unsigned len_k = conv_base_3(km1, MAX_N_LOG_3, dig_k);
// calculate C(n - 1, k - 1) mod 3
unsigned Cnk_mod3 = lucas_3(len_n, dig_n, len_k, dig_k);
// add using the modulo rule
sum = (sum + Cnk_mod3 * char_2_int(input[km1])) % 3;
}
// value of (-1) ** (n - 1)
// (no need for pow; just need to know if n is odd or even)
int sign = (n % 2) * 2 - 1;
// for negative numbers, must resolve the difference
// between C's % operator and mathematical mod
int sum_mod3 = (3 + (sign * (int)(sum % 3)) % 3;
return int_2_char(sum_mod3);
}
上記のコードはすべてのテストに合格しています。パフォーマンスではなく、わかりやすさを優先して書かれていることに注意してください。
では、なぜこのコードは割り当てられた時間内にすべてのテストに合格することができたのでしょうか。一方、単純なテーブルベースのアプローチでは合格できなかったのです。それは、その 時間の複雑さ :
-
テーブルベースのアプローチでは、三角形のすべてのレベルを処理するため、次のようになります。
O(n^2)
(参照 三角形の数字 ). -
もちろん、ルーカスのアルゴリズムを使えば、トップレベルのみを処理すればよいのですが、アルゴリズム自体は
O(log n)
のすべての桁をループするのでn
(基数は関係なく)です。全体の複雑さはO(n log n)
それでも大幅な改善です。
関連
-
[解決済み】Cコンパイルエラー。"変数サイズのオブジェクトが初期化されていない可能性がある"
-
[解決済み】ISO C90では、C言語での宣言とコードの混在が禁止されています。
-
[解決済み】スレッド1:EXC_BAD_ACCESS(コード=1、アドレス=0x0)標準Cメモリ問題
-
[解決済み] struct has no member named
-
[解決済み】警告:組み込み関数'printf'の非互換な暗黙の宣言(デフォルトで有効]
-
[解決済み】 「配列のイニシャライザーはイニシャライザーリストまたは文字列リテラルでなければなりません」と表示されるのですが?
-
[解決済み] エラー:整数が期待されるところで集約値が使用された
-
[解決済み】int型配列へのポインタのスカラ・イニシャライザの過剰要素
-
[解決済み】シンプルなC言語のscanfが機能しない?重複
-
[解決済み】whileループの時間複雑性(Big O)はどうやったらわかるの?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】stdinとSTDIN_FILENOの違いは何ですか?
-
[解決済み】エラー:cの入力の最後に期待される宣言またはステートメント
-
[解決済み】Valgrind が "Invalid write of size 8" で文句を言う。
-
[解決済み】ISO C90では、C言語での宣言とコードの混在が禁止されています。
-
[解決済み】C言語で浮動小数点例外(コアダンプ)発生
-
[解決済み】Cygwin - Makefile-error: ターゲット `main.o' のレシピに失敗しました。
-
[解決済み] テスト
-
[解決済み】MB/sとMiB/sを計算する方法は?
-
[解決済み】インクリメントオペランドとして lvalue が必要です。
-
[解決済み】c - 警告:関数 'printf'の暗黙の宣言