Дополнил описание галадки Шеннона
This commit is contained in:
parent
60c202325e
commit
d4c1dc743b
6
npm-shrinkwrap.json
generated
6
npm-shrinkwrap.json
generated
@ -4,6 +4,12 @@
|
|||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@anwinged/predictor": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@anwinged/predictor/-/predictor-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-817M9xiPesxLtvUH/qZNs3EBNw5HBOR/W8T3HYLqyNvMRCEJ/h86uJJB5BW+FzZc+mQsVQcRX+NI8pSOBr3jwg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@babel/code-frame": {
|
"@babel/code-frame": {
|
||||||
"version": "7.8.0",
|
"version": "7.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.0.tgz",
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Homepage",
|
"description": "Homepage",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@anwinged/predictor": "^0.2.1",
|
||||||
"@babel/core": "^7.8.0",
|
"@babel/core": "^7.8.0",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.8.0",
|
"@babel/plugin-proposal-class-properties": "^7.8.0",
|
||||||
"@babel/plugin-transform-runtime": "^7.8.0",
|
"@babel/plugin-transform-runtime": "^7.8.0",
|
||||||
|
@ -8,17 +8,130 @@ scripts:
|
|||||||
- /static/predictor.js
|
- /static/predictor.js
|
||||||
---
|
---
|
||||||
|
|
||||||
Правила игры простые. Робот пытается предсказать, что выберет человек: чет или нечет.
|
В студенческое время я наткнулся на интересную статью об [игре "Чет-нечет"](game)
|
||||||
Если предсказание удалось, робот получает очко, а счет уменьшается на единицу.
|
на домашней страничке пользователя [ltwood](ltwood).
|
||||||
Если же предугадать выбор не удалось, то очко достается человеку, а счет увеличивается.
|
|
||||||
Игрок победит, если доберется до 50 очков. Но когда счет опустится до -50, победа будет
|
|
||||||
за железякой.
|
|
||||||
|
|
||||||
Чтобы выбирать вариант с клавиатуры, кликните внутри серой рамки,
|
Правила очень простые. Игрок загадывает один вариант из двух: "чет" или "нечет",
|
||||||
а потом пользуйтесь клавишами "1" - нечет или "2" - чет.
|
а оппонент пытается угадать выбор игрока. Если угадать не удалось, то очко получает
|
||||||
|
загадавший, а если угадать получилось - то угадывающий. Кто первым наберет 20 очков,
|
||||||
|
тот и молодец!
|
||||||
|
|
||||||
- [Код гадалки](https://github.com/anwinged/homepage/blob/master/source/_assets/predictor/demo.vue)
|
Кажется, что в этой игре все случайно. Случайно загадывается число, потом случайно
|
||||||
|
второй игрок пытается угадать что же было загадано. Я очень сильно удивился, когда
|
||||||
|
попробовал поиграть в эту игру с программой и за десять попыток так ни разу и не выиграл.
|
||||||
|
|
||||||
|
Парадокс в том, что мы _думаем_ что загадываем числа случайно. На самом деле все не так,
|
||||||
|
и последовательность загаданных чисел не случайна.
|
||||||
|
|
||||||
|
Исходного кода оригинальной гадалки в открытом доступе нет, есть только [описание алгоритма](algo),
|
||||||
|
по которому я сделал свою реализацию на TypeScrypt.
|
||||||
|
|
||||||
|
## Демоверсия
|
||||||
|
|
||||||
|
Попробуйте набрать 50 очков и выиграть. Чтобы выбирать вариант с клавиатуры,
|
||||||
|
кликните внутри серой рамки, а потом пользуйтесь клавишами "1" - нечет или "2" - чет.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Как Это работает
|
||||||
|
|
||||||
|
Математически алгоритм на [странице](algo) сайта ltwood.
|
||||||
|
Я рассмотрю простой пример, чтобы показать принцип.
|
||||||
|
|
||||||
|
В основе алгоритма находится популяция "демонов" - автоматов, которые на основании ходов
|
||||||
|
игрока и предсказанных значениях выдают новое предсказание. Демонами управляет
|
||||||
|
супервайзер. Задача супервайзера в том, чтобы опросить всех демонов, выбрать ответ
|
||||||
|
от одного их них, а после получения ответа игрока пометить тех, кто выдал правильный ответ.
|
||||||
|
|
||||||
|
Алгоритм состоит из двух шагов:
|
||||||
|
|
||||||
|
- предсказать следующих ход игрока;
|
||||||
|
- учесть реальный ход игрока, добавив веса тому демону, который предугадал ход.
|
||||||
|
|
||||||
|
Рассмотрим работу на примере одного демона.
|
||||||
|
|
||||||
|
Пусть у нас есть демон, который смотрит на последний хода игрока
|
||||||
|
и на свое последнее предсказание.
|
||||||
|
|
||||||
|
Строим два вектора:
|
||||||
|
|
||||||
|
- `[<1 ход демона>, <1 ход игрока>, 0]`
|
||||||
|
- `[<1 ход демона>, <1 ход игрока>, 1]`
|
||||||
|
|
||||||
|
В самом начале, когда у демона нет никакой информации о ходах игрока, эти векторы
|
||||||
|
будут выглядеть как `[0]` и `[1]`. Но с накоплением данных, они всегда будут каждый
|
||||||
|
по 5 элементов.
|
||||||
|
|
||||||
|
После чего смотрим, который из таких наборов в прошлом приносил победу чаще,
|
||||||
|
и соответственно выбираем или вариант с 0, или с 1.
|
||||||
|
|
||||||
|
После получения действительного хода игрока, мы увеличиваем вес того набора,
|
||||||
|
который оказался верным. И далее снова предсказываем ход.
|
||||||
|
|
||||||
|
Теперь с числами.
|
||||||
|
|
||||||
|
#### Ход 1
|
||||||
|
|
||||||
|
У демона нет информации, наборы `[0]` и `[1]` равнозначны, выбираем `[0]`,
|
||||||
|
а значит предсказываем ход игрока 0.
|
||||||
|
|
||||||
|
Игрок загадывал 1. Обновляем веса:
|
||||||
|
|
||||||
|
```
|
||||||
|
[0] = 0
|
||||||
|
[1] = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Ход 2
|
||||||
|
|
||||||
|
Строим векторы на основе последних ходов:
|
||||||
|
|
||||||
|
```
|
||||||
|
0: [0, 1, 0]
|
||||||
|
1: [0, 1, 1]
|
||||||
|
```
|
||||||
|
|
||||||
|
Для этих векторов тоже еще нет весов, так что снова выбираем первый, предсказываем 0.
|
||||||
|
|
||||||
|
Игрок снова выбрал 1. Обновляем веса (помним, что еще были прошлые вектора из одного элемента):
|
||||||
|
|
||||||
|
```
|
||||||
|
[0] = 0
|
||||||
|
[1] = 1
|
||||||
|
[0, 1, 0] = 0
|
||||||
|
[0, 1, 1] = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Ход 3
|
||||||
|
|
||||||
|
Картина такая же, как на втором ходу, но отличие в том, что у нас есть веса с прошлого хода:
|
||||||
|
|
||||||
|
```
|
||||||
|
0: [0, 1, 0] - 0
|
||||||
|
1: [0, 1, 1] - 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Выбираем вариант 1, игрок снова выбирает 1. Предсказание удалось!
|
||||||
|
|
||||||
|
## Расширение алгоритма
|
||||||
|
|
||||||
|
Это был самый элементарный вариант. Понятно, что на таком далеко не уедешь,
|
||||||
|
и никого не обыграешь. Чтобы хорошо предугадывать ходы игроков, используется
|
||||||
|
несколько демонов с разной величиной просматриваемой истории. Следит за ними
|
||||||
|
"супервайзер", который ведет для каждого демона рейтинг. На основе этого рейтинга
|
||||||
|
выбираются ответы тех демонов, которые были наиболее успешны в своих предсказаниях.
|
||||||
|
|
||||||
|
## Ссылки
|
||||||
|
|
||||||
|
- [Код гадалки](repo)
|
||||||
|
- [Описание алгоритма](algo)
|
||||||
|
- [Описание игры у ltwood](game)
|
||||||
|
|
||||||
|
[ltwood]: https://sites.google.com/site/ltwood/
|
||||||
|
[game]: https://sites.google.com/site/ltwood/projects/heshby
|
||||||
|
[algo]: https://sites.google.com/site/ltwood/projects/heshby/algorithm
|
||||||
|
[repo]: https://github.com/anwinged/predictor
|
||||||
|
@ -1,145 +0,0 @@
|
|||||||
const DEFAULT_EPSILON = 0.01;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number[]} steps
|
|
||||||
*
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
function create_key(steps) {
|
|
||||||
return steps.join(':');
|
|
||||||
}
|
|
||||||
|
|
||||||
class Daemon {
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
base;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
humanCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
robotCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
epsilon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
weights = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number} base
|
|
||||||
* @param {Number} humanCount
|
|
||||||
* @param {Number} robotCount
|
|
||||||
* @param {Number} epsilon
|
|
||||||
*/
|
|
||||||
constructor(base, humanCount, robotCount, epsilon = DEFAULT_EPSILON) {
|
|
||||||
this.base = base;
|
|
||||||
this.humanCount = humanCount;
|
|
||||||
this.robotCount = robotCount;
|
|
||||||
this.epsilon = epsilon;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
get power() {
|
|
||||||
return this.humanCount + this.robotCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Journal} journal
|
|
||||||
*
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
predict(journal) {
|
|
||||||
const steps = this._getStepSlice(journal);
|
|
||||||
|
|
||||||
const proposals = [];
|
|
||||||
for (let i = 0; i < this.base; ++i) {
|
|
||||||
proposals[i] = this._getWeight([...steps, i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxWeight = Math.max(...proposals);
|
|
||||||
|
|
||||||
return proposals.indexOf(maxWeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Journal} journal
|
|
||||||
* @param {Number} humanValue
|
|
||||||
*/
|
|
||||||
adjust(journal, humanValue) {
|
|
||||||
const steps = this._getStepSlice(journal);
|
|
||||||
const adjustmentWeight = this._getAdjustmentWeight(journal.length);
|
|
||||||
this._adjustWeight([...steps, humanValue], adjustmentWeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Journal} journal
|
|
||||||
*
|
|
||||||
* @returns {Number[]}
|
|
||||||
*/
|
|
||||||
_getStepSlice(journal) {
|
|
||||||
return journal.getLastMovements(this.humanCount, this.robotCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number} stepNumber
|
|
||||||
*
|
|
||||||
* @returns {Number}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_getAdjustmentWeight(stepNumber) {
|
|
||||||
return Math.pow(1 + this.epsilon, stepNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number[]} steps
|
|
||||||
*
|
|
||||||
* @returns {Number}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_getWeight(steps) {
|
|
||||||
const key = create_key(steps);
|
|
||||||
const weight = this.weights[key];
|
|
||||||
return weight === undefined ? 0 : weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number[]} steps
|
|
||||||
* @param {Number} value
|
|
||||||
*
|
|
||||||
* @returns {Number}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_setWeight(steps, value) {
|
|
||||||
const key = create_key(steps);
|
|
||||||
this.weights[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number[]} steps
|
|
||||||
* @param {Number} weight
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_adjustWeight(steps, weight) {
|
|
||||||
const currentWeight = this._getWeight(steps);
|
|
||||||
const newWeight = currentWeight + weight;
|
|
||||||
this._setWeight(steps, newWeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Daemon;
|
|
@ -1,47 +0,0 @@
|
|||||||
import Move from './Move';
|
|
||||||
|
|
||||||
class Journal {
|
|
||||||
/**
|
|
||||||
* @type {Move[]}
|
|
||||||
*/
|
|
||||||
moves = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Move[]} moves
|
|
||||||
*/
|
|
||||||
constructor(moves = []) {
|
|
||||||
this.moves = moves;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number} human
|
|
||||||
* @param {Number} robot
|
|
||||||
*/
|
|
||||||
makeMove(human, robot) {
|
|
||||||
this.moves.push(new Move(human, robot));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number} humanCount
|
|
||||||
* @param {Number} robotCount
|
|
||||||
*
|
|
||||||
* @returns {Number[]}
|
|
||||||
*/
|
|
||||||
getLastMovements(humanCount, robotCount) {
|
|
||||||
const humanMoves = this.moves.map(m => m.human);
|
|
||||||
const robotMoves = this.moves.map(m => m.robot);
|
|
||||||
return [].concat(
|
|
||||||
robotMoves.slice(-robotCount),
|
|
||||||
humanMoves.slice(-humanCount)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
get length() {
|
|
||||||
return this.moves.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Journal;
|
|
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* Represents one game move.
|
|
||||||
*/
|
|
||||||
class Move {
|
|
||||||
/**
|
|
||||||
* @param {Number} human
|
|
||||||
* @param {Number} robot
|
|
||||||
*/
|
|
||||||
constructor(human, robot) {
|
|
||||||
this.human = human;
|
|
||||||
this.robot = robot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Move;
|
|
@ -1,90 +0,0 @@
|
|||||||
import Daemon from './Daemon';
|
|
||||||
import Journal from './Journal';
|
|
||||||
import Supervisor from './Supervisor';
|
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {
|
|
||||||
base: 2,
|
|
||||||
supervisor_epsilon: 0.01,
|
|
||||||
daemons: [
|
|
||||||
{ human: 2, robot: 2, epsilon: 0.01 },
|
|
||||||
{ human: 3, robot: 3, epsilon: 0.01 },
|
|
||||||
{ human: 4, robot: 4, epsilon: 0.01 },
|
|
||||||
{ human: 5, robot: 5, epsilon: 0.01 },
|
|
||||||
{ human: 6, robot: 6, epsilon: 0.01 },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Predictor {
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
base;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
score;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Journal}
|
|
||||||
*/
|
|
||||||
journal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Supervisor}
|
|
||||||
*/
|
|
||||||
supervisor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Object} config
|
|
||||||
*/
|
|
||||||
constructor(config = DEFAULT_CONFIG) {
|
|
||||||
this.base = config.base;
|
|
||||||
this.score = 0;
|
|
||||||
this.journal = new Journal();
|
|
||||||
const daemons = this._createDaemons(config.daemons);
|
|
||||||
this.supervisor = new Supervisor(daemons, config.supervisor_epsilon);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number|String} humanValue
|
|
||||||
*
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
pass(humanValue) {
|
|
||||||
const value = parseInt(humanValue, 10);
|
|
||||||
if (value < 0 || value >= this.base) {
|
|
||||||
throw new Error(`Passed value must be in [0, ${this.base})`);
|
|
||||||
}
|
|
||||||
const prediction = this.supervisor.predict(this.journal);
|
|
||||||
this.score += prediction === value ? -1 : 1;
|
|
||||||
this.supervisor.adjust(this.journal, value);
|
|
||||||
this.journal.makeMove(value, prediction);
|
|
||||||
return prediction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Object} daemonConfigs
|
|
||||||
*
|
|
||||||
* @returns {Daemon[]}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_createDaemons(daemonConfigs) {
|
|
||||||
return daemonConfigs.map(config => {
|
|
||||||
return new Daemon(
|
|
||||||
this.base,
|
|
||||||
config.human,
|
|
||||||
config.robot,
|
|
||||||
config.epsilon || 0.01
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
stepCount() {
|
|
||||||
return this.journal.length;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
const DEFAULT_EPSILON = 0.01;
|
|
||||||
|
|
||||||
class Supervisor {
|
|
||||||
/**
|
|
||||||
* @type {{daemon: Daemon, rate: Number}[]}
|
|
||||||
*/
|
|
||||||
daemons = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
epsilon;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Daemon[]} daemons
|
|
||||||
* @param {Number} epsilon
|
|
||||||
*/
|
|
||||||
constructor(daemons, epsilon = DEFAULT_EPSILON) {
|
|
||||||
if (!daemons || daemons.length === 0) {
|
|
||||||
throw Error('Empty daemon list');
|
|
||||||
}
|
|
||||||
this.daemons = daemons.map(daemon => ({
|
|
||||||
daemon: daemon,
|
|
||||||
rate: 0,
|
|
||||||
}));
|
|
||||||
this.epsilon = epsilon;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Journal} journal
|
|
||||||
*
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
predict(journal) {
|
|
||||||
const predictions = this._createPredictions(journal);
|
|
||||||
const ordered = this._sortPredictions(predictions);
|
|
||||||
|
|
||||||
return ordered[0].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Journal} journal
|
|
||||||
* @param {Number} humanValue
|
|
||||||
*/
|
|
||||||
adjust(journal, humanValue) {
|
|
||||||
const predictions = this._createPredictions(journal);
|
|
||||||
for (const prediction of predictions) {
|
|
||||||
if (prediction.value === humanValue) {
|
|
||||||
prediction.daemon.rate += this._getAdjustmentWeight(
|
|
||||||
journal.length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
prediction.daemon.daemon.adjust(journal, humanValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Journal} journal
|
|
||||||
*
|
|
||||||
* @returns {Array}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_createPredictions(journal) {
|
|
||||||
return this.daemons.map(daemon => ({
|
|
||||||
daemon: daemon,
|
|
||||||
power: daemon.daemon.power,
|
|
||||||
rate: daemon.rate,
|
|
||||||
value: daemon.daemon.predict(journal),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Array} predictions
|
|
||||||
*
|
|
||||||
* @returns {Array}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_sortPredictions(predictions) {
|
|
||||||
return predictions.sort((result1, result2) => {
|
|
||||||
const rateDiff = result2.rate - result1.rate;
|
|
||||||
if (Math.abs(rateDiff) > 0.000001) {
|
|
||||||
return rateDiff;
|
|
||||||
}
|
|
||||||
return result1.power - result2.power;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number} stepNumber
|
|
||||||
*
|
|
||||||
* @returns {Number}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_getAdjustmentWeight(stepNumber) {
|
|
||||||
return Math.pow(1 + this.epsilon, stepNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Supervisor;
|
|
@ -39,7 +39,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Predictor from './Predictor';
|
import Predictor from '@anwinged/predictor';
|
||||||
|
|
||||||
const MAX_SCORE = 50;
|
const MAX_SCORE = 50;
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* const prediction = */ this.predictor.pass(value);
|
/* const prediction = */ this.predictor.pass(+value);
|
||||||
},
|
},
|
||||||
restart() {
|
restart() {
|
||||||
this.predictor = make_predictor();
|
this.predictor = make_predictor();
|
||||||
|
Loading…
Reference in New Issue
Block a user