中國余數(shù)定理(Chinese Remainder Theorem)

前言


中國余數(shù)定理也叫孫子定理記錄在《孫子算經(jīng)》中:“今有物不知其數(shù),三三數(shù)之剩二(除以3余2),五五數(shù)之剩三(除以5余3),七七數(shù)之剩二(除以7余2),問物幾何?”

關(guān)于孫子算經(jīng)這道題的解法宋朝數(shù)學家秦九韶于1247年《數(shù)書九章》卷一、二《大衍類》對“物不知數(shù)”問題做出了完整系統(tǒng)的解答。明朝數(shù)學家程大位將解法編成易于上口的《孫子歌訣》:

三人同行七十稀,五樹梅花廿一支,七子團圓正半月,除百零五使得知。

即:先找到5與7公倍數(shù)中余3為1的數(shù)=70,3與7公倍數(shù)中余5為1的數(shù)=21,3與5公倍數(shù)中余7為1的數(shù)=15,然后將70 * 2(除以3余2)+21 * 3(除以5余3)+15 * 2(除以7余2)=233,最后233%(3 * 5 * 7=105)=23。

接下來記錄一道用到中國余數(shù)定理的題目。

題目(來源GCJ2019 1A Golf Gophers)


Problem

Last year, a bunch of pesky gophers took up residence in our orchard. We tried to change our line of work by opening up a miniature golf course, but it looks like the gophers have followed us here! Once again, we need to figure out how many gophers there are, but we cannot observe them directly because they are secretive and nocturnal, whereas we like to sleep at night. We do know there are between 1 and M gophers, inclusive.

Our mini golf course is famous for having a small electronic windmill on each of its 18 holes. The i-th windmill has 2 ≤ Bi ≤ 18 blades, which are numbered from 0 to Bi-1, clockwise. Each night, before going to sleep, we turn off the windmills and set each one such that blade 0 is pointing downward, which is important so that the windmills can charge up properly for the next day. However, we have noticed that when we wake up, the windmills have been disturbed. Since our mini golf course is in a windless area, we think the mischievous gophers must be responsible!

We know that every night, all of the gophers emerge, one by one; each of them chooses one of the windmills independently and uniformly at random and rotates it counterclockwise by one blade. So, for example, for a windmill with 3 blades for which 0 is pointing downward, the first gopher to interact with it turns it so that 1 is pointing downward, and then the next gophers to interact with that windmill make the downward-pointing blade have number 2, then 0, then 1, and so on.

We have devised a plan. We designed our windmills so that we can easily change the number of blades (to modulate the difficulty of our course), and we will now take advantage of this! Each night, before going to sleep, we can choose the number of blades on each of the 18 windmills, within the given limits; we do not have to use the same number of blades on each windmill, or make the same choices every night. In the morning, we will observe the number on each windmill's downward-pointing blade.

We have N nights in which to figure out G, the number of gophers. Can you help us?

Input and output

This is an interactive problem. You should make sure you have read the information in the Interactive Problems section of our FAQ.

Initially, your program should read a single line containing three integers T, N and M, the number of test cases, the number of nights allowed per test case and the maximum number of gophers, respectively. Then, you need to process T test cases.

In each test case, your program processes up to N + 1 exchanges with our judge. You may make up to N exchanges of the following form:

  • Your program outputs one line with eighteen integers between 2 and 18, inclusive; the i-th of these represents the number of blades you want the i-th windmill to have on that night.
  • The judge responds with one line with eighteen integers; the i-th of these represents the number on the downward-pointing blade of the i-th windmill in the morning, after the gophers have worked their mischief. If you sent invalid data (e.g., a number out of range, or a malformed line), the judge instead responds with -1.

On each night, for each gopher, the choice of which windmill the gopher turns is uniform at (pseudo)-random, and independent of any other choice by any gopher (including itself) on any night.

After making between 0 and N exchanges as explained above, you must make one more exchange of the following form:

  • Your program outputs one integer: your guess for G, the number of gophers.
  • The judge responds with one line with a single integer: 1 if your answer is correct, and -1 if it is not (or you have provided a malformed line).

After the judge sends -1 to your input stream (because of either invalid data or an incorrect answer), it will not send any other output. If your program continues to wait for the judge after receiving -1, your program will time out, resulting in a Time Limit Exceeded error. Notice that it is your responsibility to have your program exit in time to receive a Wrong Answer judgment instead of a Time Limit Exceeded error. As usual, if the memory limit is exceeded, or your program gets a runtime error, you will receive the appropriate judgment.

Limits

1 ≤ T ≤ 20.
Time limit: 20 seconds per test set.
Memory limit: 1GB.

Test set 1 (Visible)

N = 365.
M = 100.

Test set 2 (Hidden)

N = 7.
M = 106.

Testing Tool

You can use this testing tool to test locally or on our servers. To test locally, you will need to run the tool in parallel with your code; you can use our interactive runner for that. For more information, read the Interactive Problems section of the FAQ.

Local Testing Tool

To better facilitate local testing, we provide you the following script. Instructions are included inside. You are encouraged to add more test cases for better testing. Please be advised that although the testing tool is intended to simulate the judging system, it is NOT the real judging system and might behave differently.

If your code passes the testing tool but fails the real judge, please check the Coding section of our FAQ to make sure that you are using the same compiler as us.

Download testing_tool.py

Sample Interaction

This interaction corresponds to Test set 1. Suppose that, unbeknownst to us, the judge has decided that there are 10 gophers.

  t, n, m = readline_int_list()   // Reads 20 into t, 365 into n and 100 into m.
  // Choose numbers of blades for day 1.
  printline 2 2 2 2 18 3 3 3 3 3 3 4 4 4 4 5 2 2 to stdout
  flush stdout
  // Reads 0 0 0 0 0 0 1 2 1 0 1 2 0 0 0 0 1 0 into res.
  res = readline_int_list()
  // Choose numbers of blades for day 2.
  printline 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 to stdout
  flush stdout
  // Reads 0 1 1 2 0 0 1 0 0 0 0 0 0 1 0 0 0 0 into res.
  res = readline_int_list()
  printline 8 to stdout        // We make a wrong guess even though we could
  flush stdout                 // have investigated for up to 363 more nights.
  verdict = readline_int()     // Reads -1 into verdict (judge has decided our
                               //   solution is incorrect)
  exit                         // Exits to avoid an ambiguous TLE error

Notice that even though the guess was consistent with the information we received from the judge, we were still wrong because we did not find the correct value.

分析


題目大意是有一群囊鼠晚上出來活動,他們會撥亂我們放置的風車葉子,并且是順時針撥亂的比如有三片標記的葉子0,1,2。一只囊鼠通過由0 ~> 1,兩只囊鼠通過就0 ~> 1 ~> 2,3只就0 ~> 1 ~> 2 ~> 0,(其實就是通過囊鼠個數(shù)求葉子的余數(shù))。并且囊鼠每晚只完全隨機選擇一個風車來撥動,下面希望在有限次設(shè)置風車下計算出囊鼠數(shù)量,風車數(shù)量固定18,每個風車葉子可以選擇[2,18]中的任意值,并且初始都是葉子都是標記0。

首先,我們可以確定我們不能每晚風車選不同數(shù)值,像Sample Interaction一樣,這樣完全隨機的返回18個余數(shù)無法利用。我們只能每晚所有風車選一樣的葉子數(shù)。比如18個18,如果運氣好(囊鼠數(shù)量很少或者很多個夜晚的話),我們有可能18個風車都沒有經(jīng)歷一個輪回,那么所有返回數(shù)值相加就是結(jié)果。

但是很不幸,對于大數(shù)據(jù)囊鼠達到106,只有7個夜晚是不可能完成的。這就需要用到中國余數(shù)定理來解決了。

這里和孫子算經(jīng)中的問題有點不同,余數(shù)數(shù)量達到了7個,但是并不影響解法原理,只需從3個余數(shù)擴展到7個。比如擴展到(3,5,7,9)四個數(shù),對與3的余數(shù)處理,我同樣找到5 * 7 * 9的公倍數(shù)中余3為1的數(shù),然后乘以3的余數(shù),最后把4個操作數(shù)加起來余3 * 5 * 7 * 9的值就得到結(jié)果。

然后回到題目中我們需要7個數(shù)的乘積>106就能保證最后值能覆蓋106所有可能。這里選擇得是{5,7,9,11,13,16,17}這7個數(shù),當然也可以選擇其他乘積更大的數(shù),比如11到17。

最后在求多個數(shù)乘積和某個數(shù)余1的數(shù)的時候可以用到擴展歐幾里德算法,公式ax=1(mod p),p是我們的選定的某個葉子數(shù),a是其他數(shù)乘積,通過擴展歐幾里德算法可以求得x(也叫乘法逆元),ax就是我們要的余p為1的值。這里不能用費馬小定理的原因是我們選擇的數(shù)當中9,16都不是素數(shù)。

解法代碼


//
//  main.cpp
//  Golf Gophers
//
//  Created by Jiao Liu on 4/22/19.
//  Copyright ? 2019 ChangHong. All rights reserved.
//

#include <iostream>

using namespace std;
int t,n,m;
int blade[7] = {5,7,9,11,13,16,17};
int tot = 5*7*9*11*13*16*17;

long long Extend_Gcd(long long a, long long b, int &x, int &y) {
    if (!b) {
        x = 1; y = 0;
        return  a;
    }
    else {
        long long r = Extend_Gcd(b, a%b, y, x);
        y -= a / b*x;
        return r;
    }
}

int main(int argc, const char * argv[]) {
    cin>>t>>n>>m;
    while (t--) {
        long long ans = 0;
        for (int d = 0; d < min(7, n); d++) {
            for (int i = 0; i < 18; i++) {
                cout<<blade[d]<<" ";
            }
            cout<<endl;
            fflush(stdout);
            long long total = 0;
            for (int i = 0; i < 18; i++) {
                int tm;
                cin>>tm;
                total += tm;
            }
            int p = tot/blade[d];
            int x,y;
            Extend_Gcd(p, blade[d], x, y);
            ans += x * total * p;
            ans %= tot;
        }
        cout<<ans%tot<<endl;
        fflush(stdout);
        int res;
        cin>>res;
        if (res != 1) {
            return 0;
        }
    }
    return 0;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內(nèi)容