Поигрался немножко на выходных с гадалкой и появилось непреодолимое желание ещё пофлудить в этой темке, хоть она, возможно, уже и успела уплыть за пределы актуальности.
2
IuraЦитата:
Нашел что искал - "Прогнозирование стохастических последовательностей"
Нашли что искать. 

 За мозгодробительными формулировками этой статьи, по-сути, скрывается оптимистичное заключение о разрешимости вашей задачи. Да и заканчивается она немного с черным юморком как-то, "Во время работы над этой статьей умер _ Колмогоров".
Цитата:
О, а мне понравился подход автора этой программульки с выбором оптимальных параметров гадалки (количества учитываемых ходов, как человека так и собственных).
К сожалению, на упомянутом вами ресурсе мне почему-то не удалось отыскать исходных текстов программы heshby (лично у автора исходники не выпрашивал, уж больно напугало меня его заявление о том, что "авторские права распространяются на использованный в программе алгоритм предикции стохастических процессов"), поэтому я все-таки попробую привести здесь возможный набросок реализации её алгоритма. 

Но сначала напишу простейший неинтерактивный вариант гадалки (одна штука; учет двух ходов с каждой стороны), играющей против генератора псевдослучайных чисел и печатающей качество предсказания в процентах (пример на C):
#include <stdio.h>
#include <stdlib.h>
int S[2][2][3][3][2], a[3], b[3];
int main(void)
{
    int i, *p, c=0;
    for(i=0; i<350; i++)
    {
        p=S[a[1]] [a[2]] [b[1]] [b[2]];
        a[0]=a[1]; a[1]=a[2]; b[0]=b[1]; b[1]=b[2];
        if((a[2]=rand()%2)==(b[2]=p[0]==p[1]?2:p[0]<p[1])) c++;
        p[a[2]]++;
    }
    printf("%d%%", 100*c/i);
    return 0;
}
 
  (Оффтоп)
Кстати, есть сомнения в правильности понимания мной алгоритма Гадалки Шеннона. Если что не так, поправьте меня, пожалуйста (человекочитабельный и обобщенный вариант, не страдающий от синдрома запиндюривания всего кода в одну строку, см. ниже).
(Оффтоп)
Младшие биты числа, возвращенного rand()'ом нешибко-то случайны. Увеличится ли "случайность" бит (эксперимент показывает, что да) если их значения получать по-прежнему как rand()%2, но предварительно делать ещё один вызов этой функции вхолостую, i.e. на деле использовать значение выражения (rand(), rand()%2)?
А вот пример (в этот раз на C++) предиктора на основе набора гадалок (с различными параметрами, и, соответственно, с различной предсказательной способностью относительно данной последовательности):
#include <deque>
#include <map>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include <cstdlib>
namespace Fun
{
    int TossACoin() {return rand()%2;}
    // "Abstract" predictor.
    class cPredictor
    {
        public:
            cPredictor():
                CorrectPredictionsCount(0),
                PredictionsCount(0),
                PredictedValue(0) {}
            int Predict() {return NewPrediction(0);};
            void Correct(int RealBit) 
            {
                if(PredictedBit()==RealBit) CorrectPrediction();
            };
            // Returns the rate (quality) of correct predictions.
            int Rate() const
            {
                if(!PredictionsCount) return 0; // Prevent div by zero.
                return 100*CorrectPredictionsCount/PredictionsCount;
            }
        protected:
            void CorrectPrediction() {CorrectPredictionsCount++;}
            int NewPrediction(int PredictedBit)
            {
                PredictionsCount++;
                return PredictedValue=PredictedBit;
            }
            int PredictedBit() const {return PredictedValue;};
        private:
            int CorrectPredictionsCount, PredictionsCount, PredictedValue;
    };
    struct cCheater: cPredictor
    {
        int Predict() {return NewPrediction(TossACoin());};
    };
    class cDaemon: public cPredictor
    {
        public:
            explicit cDaemon
                (
                    int RealBits=2,
                    int PredictedBits=2,
                    // Factor of "forgetfulness."
                    float CorrectingFactor=1,
                    // Minimal difference between probabilities.
                    float Threshold=0
                ):
                    LastBits(RealBits+PredictedBits+2, 0),
                    CorrectingFactor(CorrectingFactor),
                    Threshold(Threshold) {}
            // Returns the most probable value of the next bit.
            int Predict()
            {
                // Forget old bits.
                LastBits.pop_front();
                LastBits.pop_front();
                // Just find a desired value in the
                // associative array Statistics.
                return NewPrediction(Statistics[LastBits].Value(Threshold));
            }
            // Adjusts the statistical model.
            void Correct(int RealBit)
            {
                if(PredictedBit()==RealBit) CorrectPrediction();
                // Remember that current real bit follows
                // the bits from LastBits.
                // FIXME: Reference to Statistics[LastBits] can be cached.
                Statistics[LastBits].Correct(RealBit, CorrectingFactor);
                // Save (in stack-like LastBits) RealBit &
                // PredictedBit for future use.
                LastBits.push_back(RealBit);
                LastBits.push_back(PredictedBit());
            }
            int Weight() const {return LastBits.size()-2;}
        private:
            // FIXME: How to "parametrize" this class with
            //        CorrectingFactor & Threshold?
            class cPrediction
            {
                public:
                    cPrediction():
                        One(0),
                        Zero(0),
                        Increment(1) {}
                    void Correct(int Bit, float CorrectingFactor)
                    {
                        // This can improve (when CorrectingFactor>1)
                        // prediction quality by reducing the
                        // influence of very old bits.
                        Increment*=CorrectingFactor;
                        // Increase the probability of a given
                        // bit at the next trials.
                        Bit? One+=Increment: Zero+=Increment;
                    }
                    int Value(float Threshold) const
                    {
                        // Try to predict the next bit by comparing
                        // probabilities of each case.
                        if(One-Zero>Threshold) return 1;
                        if(Zero-One>Threshold) return 0;
                        return 2; // Uncertain result.
                    }
                private:
                    float One, Zero, Increment, CorrectingFactor, Threshold;
            };
            // FIXME: There is a more efficient way to store these bits.
            typedef std::deque<int> cBits;
            // FIXME: A container std::map is too slow, use
            //        something like hash_map instead.
            typedef std::map<cBits, cPrediction> cStatistics;
            // The stochastic model of an input sequence.
            cStatistics Statistics;
            // Last bits, both real and predicted.
            cBits LastBits;
            float CorrectingFactor, Threshold;
    };
    // FIXME: What about multithreading?
    class cSupervisor: public cPredictor
    {
        public:
            explicit cSupervisor
                (
                    int MaximalRealBits,
                    int MaximalPredictedBits,
                    bool OptimizeCorrectingFactor=false
                )
            {
                // Create daemons with all possible
                // values of the parameters.
                // FIXME: Does the STL provide means to
                //        deal with Cartesian product?
                for(int i=0; i<MaximalRealBits; i++)
                    for(int j=0; j<MaximalPredictedBits; j++)
                        if(OptimizeCorrectingFactor)
                            for(float k=1; k<2; k+=0.05)
                                Daemons.push_back(cDaemon(i, j, k));
                        else
                            Daemons.push_back(cDaemon(i, j));
            }
            int Predict()
            {
                // Find the best daemon and ask for prediction.
                // FIXME: Function std:: max_element is very suitable here
                //        but it's so difficult to express appropriate
                //        comparison criterion...
                return NewPrediction(std::accumulate
                    (
                        Daemons.begin(),
                        Daemons.end(),
                        (int)2,
                        Rating()
                    ));
            }
            void Correct(int RealBit)
            {
                if(PredictedBit()==RealBit) CorrectPrediction();
                // Simply dispatch RealBit to each daemon.
                std::for_each
                    (
                        Daemons.begin(),
                        Daemons.end(),
                        std::bind2nd // Yes, it's currying.
                            (
                                std::mem_fun_ref(&cDaemon::Correct),
                                RealBit
                            )
                    );
            }
        private:
            std::vector<cDaemon> Daemons;
            struct Rating: std::binary_function<int, cDaemon, int>
            {
                Rating(): Rate(-1), Weight(0) {}
                int operator () (int LastPrediction, cDaemon &Daemon)
                {
                   int Rate=Daemon.Rate(),
                       Weight=Daemon.Weight(),
                       Prediction=Daemon.Predict();
                   if(Rate>this->Rate||Rate==this->Rate&&Weight<this->Weight)
                   {
                       // Store the best results.
                       this->Rate=Rate;
                       this->Weight=Weight;
                       return Prediction;
                   }
                   return LastPrediction;
                }
                private: int Rate, Weight;
            };
    };
}
 
  N.B., это очень "тормознутая" реализация и предназначена только для демонстрации. Буду рад если кто-нибудь перепишет/исправит/доработает приведенный мной код.
Третий параметр (
CorrectingFactor) в конструкторе 
cDaemon'а  позволяет улучшить качество предсказания за счет забывания начала игры (значение параметра для этого должно быть немного больше единицы). Четвертый параметр (
Threshold) равен минимальной разнице между вероятностями значений следующего хода, т.е., если вероятности (частоты) различаются незначительно, то гадалка дает неопределнный ответ (по-умолчанию этот параметр равен нулю и неопределенный ответ дается только в случае полной равноправности ожидаемых значений предсказываемого хода).
Пример игры против ГПСЧ:
#include <iostream>
const int IterationsCount=350;
int main()
{
    Fun::cSupervisor Supervisor(5, 5, true);
    for(int Counter=0; Counter<IterationsCount; Counter++)
    {
        Supervisor.Predict();
        Supervisor.Correct(Fun::TossACoin());
    }
    std::cout << "Prediction quality is "
        << Supervisor.Rate() << '%' << std::endl;
    return 0;
}
 
  В только что приведеном примере можно заменить 
cSupervisor на 
cCheater (i.e. предиктор, пытающийся угадать последовательность "подбрасыванием монетки")  и убедиться, что при игре ГПСЧ vs. ГПСЧ качество угадывание составляет 50% (вполне логично). Также можно заменить 
cSupervisor на 
cDaemon и увидеть, что качество предсказаний уменьшилось (опять ожидаемый результат).
Пример игры против человека:
#include <iostream>
int main()
{
    Fun::cSupervisor Supervisor(5, 5, true);
    while(1)
    {
        int Prediction=Supervisor.Predict();
        std::cout << "> ";
        int Real;
        std::cin >> Real;
        Supervisor.Correct(Real);
        std::cout << "Prediction: " << Prediction << std::endl;
    }
    return 0;
}
 
  P.S.:
Немного отойду от основной темы. Все тот-же 
ltwood, автор heshby, утверждает, что человеку обыграть его программку не так-то просто. Ok, согласен. Также программульке туго приходится против ГПСЧ. Это понятно. Но, мне кажется, простой способ почти честно обыграть heshby существует и он очень прост. Действительно, что если заставить играть гадалку против аналогичной гадалки, но инвертирующей свои предсказания?
То есть, чтобы победить, просто запускаем две копии программки и пытаемся предсказать с помощью одной из них предсказания другой, после чего вбиваем противоположный ответ в атакуемую гадалку. 

 Возможна ли такая стратегия (типа антигравитации Мюнхгаузена)?
В любом случае, представляется разумной концепция игры одного предсказателя против другого. В общем, что если написать некую функцию 
Fun::Play(...), которая будет принимать в своих аргументах двух предсказателей и возвращать качество игры, ну допустим, первого из них? Можно, например, заставить играть 
cCheater'а против себя: 
Fun::Play(Fun::cCheater(), Fun::cCheater(), ...); или устроить поединок 
cDaemon vs. 
cCheater: 
Fun::Play(Fun::cDaemon(10, 10), Fun::cCheater(), ...); или же организовать игру с человеком: 
Fun::Play(Fun::cSupervisor(5, 5, true), Fun::сPerson()). Я не реализовал пока такую функцию из-за сложностей с неопределенными ответами (ответ 2).
P.P.S.:
Вообще, думаю, тема предсказания (экстраполяции) последовательностей далеко не исчерпывается простейшими алгоритмами вроде Гадалки Шеннона и дискуссия может быть продолжена.
(Оффтоп)
А нельзя ли темку двинуть в дискуссионные или в cs (после чего удалить эту просьбу 

 )?