บทนำ
ถ้าฉันเข้าใจคุณถูกต้องคุณต้องระบุผู้ใช้ที่คุณไม่มีรหัสประจำตัวดังนั้นคุณจึงต้องการทราบว่าพวกเขาเป็นใครโดยการจับคู่ข้อมูลแบบสุ่ม คุณไม่สามารถจัดเก็บข้อมูลประจำตัวของผู้ใช้ได้อย่างน่าเชื่อถือเนื่องจาก:
- สามารถลบคุกกี้ได้
- ที่อยู่ IP สามารถเปลี่ยนแปลงได้
- เบราว์เซอร์สามารถเปลี่ยนแปลงได้
- แคชเบราว์เซอร์อาจถูกลบ
Java Applet หรือ Com Object น่าจะเป็นวิธีแก้ปัญหาที่ง่ายโดยใช้แฮชข้อมูลฮาร์ดแวร์ แต่ทุกวันนี้ผู้คนตระหนักถึงความปลอดภัยมากว่าจะยากที่จะให้ผู้ใช้ติดตั้งโปรแกรมประเภทนี้ในระบบของตน สิ่งนี้ทำให้คุณติดอยู่กับการใช้คุกกี้และเครื่องมืออื่น ๆ ที่คล้ายคลึงกัน
คุกกี้และเครื่องมืออื่น ๆ ที่คล้ายคลึงกัน
คุณอาจพิจารณาการสร้างข้อมูลโปรไฟล์แล้วใช้การทดสอบความน่าจะเป็นในการระบุผู้ใช้น่าจะเป็น โปรไฟล์ที่มีประโยชน์สำหรับสิ่งนี้สามารถสร้างขึ้นได้จากการรวมกันของสิ่งต่อไปนี้:
- ที่อยู่ IP
- ที่อยู่ IP จริง
- ที่อยู่ IP ของพร็อกซี (ผู้ใช้มักใช้พร็อกซีเดิมซ้ำ ๆ )
- คุ้กกี้
- ข้อบกพร่องของเว็บ (เชื่อถือได้น้อยลงเนื่องจากข้อบกพร่องได้รับการแก้ไข แต่ก็ยังมีประโยชน์)
- PDF Bug
- แฟลชบั๊ก
- Java Bug
- เบราว์เซอร์
- HTML5 และ Javascript
- HTML5 LocalStorage
- HTML5 Geolocation API และ Reverse Geocoding
- สถาปัตยกรรม, ภาษาของระบบปฏิบัติการ, เวลาของระบบ, ความละเอียดหน้าจอ ฯลฯ
- API ข้อมูลเครือข่าย
- API สถานะแบตเตอรี่
แน่นอนว่ารายการที่ฉันระบุไว้นั้นเป็นเพียงไม่กี่วิธีที่เป็นไปได้ที่ผู้ใช้สามารถระบุได้โดยไม่ซ้ำกัน ยังมีอีกมากมาย
ด้วยองค์ประกอบข้อมูลสุ่มชุดนี้ในการสร้างโปรไฟล์ข้อมูลมีอะไรต่อไป
ขั้นตอนต่อไปคือการพัฒนาFuzzy Logicหรือที่ดีกว่านั้นคือArtificial Neural Network (ซึ่งใช้ตรรกะคลุมเครือ) ไม่ว่าในกรณีใดแนวคิดคือการฝึกอบรมระบบของคุณจากนั้นรวมการฝึกอบรมกับการอนุมานแบบเบย์เพื่อเพิ่มความแม่นยำของผลลัพธ์ของคุณ
NeuralMeshห้องสมุดสำหรับ PHP ช่วยให้คุณสามารถสร้างโครงข่ายประสาทเทียม หากต้องการใช้ Bayesian Inference โปรดดูลิงก์ต่อไปนี้:
ณ จุดนี้คุณอาจคิดว่า:
ทำไมคณิตศาสตร์และลอจิกมากมายสำหรับงานที่ดูเหมือนง่าย?
โดยทั่วไปเพราะมันเป็นไม่ได้เป็นงานที่ง่าย สิ่งที่คุณพยายามจะบรรลุคือความน่าจะเป็นที่แท้จริง ตัวอย่างเช่นให้ผู้ใช้ที่รู้จักดังต่อไปนี้:
User1 = A + B + C + D + G + K
User2 = C + D + I + J + K + F
เมื่อคุณได้รับข้อมูลต่อไปนี้:
B + C + E + G + F + K
คำถามที่คุณถามเป็นหลักคือ:
ความน่าจะเป็นที่ข้อมูลที่ได้รับ (B + C + E + G + F + K) เป็น User1 หรือ User2 เป็นอย่างไร และสองแมตช์ใดที่น่าจะเป็นไปได้มากที่สุด ?
เพื่อให้ตอบคำถามนี้ได้อย่างมีประสิทธิภาพคุณต้องเข้าใจความถี่เทียบกับรูปแบบความน่าจะเป็นและสาเหตุที่ความน่าจะเป็นร่วมอาจเป็นแนวทางที่ดีกว่า รายละเอียดมีมากเกินไปที่จะเข้ามาที่นี่ (ซึ่งเป็นสาเหตุที่ฉันให้ลิงก์แก่คุณ) แต่ตัวอย่างที่ดีคือแอปพลิเคชันตัวช่วยสร้างการวินิจฉัยทางการแพทย์ซึ่งใช้การรวมกันของอาการเพื่อระบุโรคที่เป็นไปได้
คิดสักครู่ของชุดของจุดข้อมูลซึ่งประกอบด้วยข้อมูลโปรไฟล์ของคุณ (B + C + E + G + F + K ในตัวอย่างข้างต้น) เป็นอาการและไม่ทราบผู้ใช้งานเป็นโรค ด้วยการระบุโรคคุณสามารถระบุวิธีการรักษาที่เหมาะสมเพิ่มเติมได้ (ถือว่าผู้ใช้รายนี้เป็น User1)
เห็นได้ชัดว่าโรคที่เราระบุได้มากกว่า 1 อาการนั้นง่ายต่อการระบุ ในความเป็นจริงยิ่งเราสามารถระบุอาการได้มากขึ้นการวินิจฉัยของเราก็จะง่ายและแม่นยำมากขึ้นเท่านั้น
มีทางเลือกอื่นหรือไม่?
แน่นอน. อีกวิธีหนึ่งคือการวัดผลคุณอาจสร้างอัลกอริทึมการให้คะแนนอย่างง่ายของคุณเองและอิงตามการจับคู่ วิธีนี้ไม่มีประสิทธิภาพเท่ากับความน่าจะเป็น แต่อาจจะง่ายกว่าสำหรับคุณในการนำไปใช้
ตัวอย่างเช่นพิจารณาแผนภูมิคะแนนง่ายๆนี้:
+ ------------------------- + -------- + ------------ +
| คุณสมบัติ | น้ำหนัก | ความสำคัญ |
+ ------------------------- + -------- + ------------ +
| ที่อยู่ IP จริง | 60 | 5 |
| ที่อยู่ IP ของพร็อกซีที่ใช้ | 40 | 4 |
| คุกกี้ HTTP | 80 | 8 |
| คุกกี้เซสชัน | 80 | 6 |
| คุกกี้ของบุคคลที่สาม | 60 | 4 |
| คุกกี้แฟลช | 90 | 7 |
| PDF Bug | 20 | 1 |
| แฟลชบั๊ก | 20 | 1 |
| Java Bug | 20 | 1 |
| หน้าเว็บบ่อย | 40 | 1 |
| เบราว์เซอร์ลายนิ้วมือ | 35 | 2 |
| ปลั๊กอินที่ติดตั้ง | 25 | 1 |
| รูปภาพแคช | 40 | 3 |
| URL | 60 | 4 |
| การตรวจหาแบบอักษรของระบบ | 70 | 4 |
| Localstorage | 90 | 8 |
| ตำแหน่งทางภูมิศาสตร์ | 70 | 6 |
| AOLTR | 70 | 4 |
| API ข้อมูลเครือข่าย | 40 | 3 |
| API สถานะแบตเตอรี่ | 20 | 1 |
+ ------------------------- + -------- + ------------ +
สำหรับข้อมูลแต่ละชิ้นที่คุณสามารถรวบรวมตามคำขอที่กำหนดให้ให้คะแนนที่เกี่ยวข้องจากนั้นใช้ความสำคัญเพื่อแก้ไขความขัดแย้งเมื่อคะแนนเท่ากัน
หลักฐานแนวคิด
สำหรับหลักฐานที่เรียบง่ายของแนวคิดโปรดดูที่Perceptron Perceptron เป็นRNA Modelที่ใช้โดยทั่วไปในแอพพลิเคชั่นจดจำรูปแบบ แม้จะมีคลาส PHPเก่าที่ใช้งานได้อย่างสมบูรณ์แบบ แต่คุณอาจต้องปรับเปลี่ยนตามวัตถุประสงค์ของคุณ
แม้จะเป็นเครื่องมือที่ยอดเยี่ยม แต่ Perceptron ยังคงสามารถส่งคืนผลลัพธ์ได้หลายรายการ (การจับคู่ที่เป็นไปได้) ดังนั้นการใช้การเปรียบเทียบคะแนนและความแตกต่างยังคงมีประโยชน์ในการระบุสิ่งที่ดีที่สุดของการแข่งขันเหล่านั้น
สมมติฐาน
- จัดเก็บข้อมูลที่เป็นไปได้ทั้งหมดเกี่ยวกับผู้ใช้แต่ละคน (IP คุกกี้ ฯลฯ )
- ในกรณีที่ผลการแข่งขันเป็นแบบตรงทั้งหมดให้เพิ่มคะแนนขึ้น 1
- ในกรณีที่ผลลัพธ์ไม่ใช่การแข่งขันแบบตรงทั้งหมดให้ลดคะแนนลง 1
ความคาดหวัง
- สร้างฉลาก RNA
- สร้างผู้ใช้แบบสุ่มจำลองฐานข้อมูล
- สร้างผู้ใช้ที่ไม่รู้จักคนเดียว
- สร้าง RNA และค่าของผู้ใช้ที่ไม่รู้จัก
- ระบบจะผสานข้อมูล RNA และสอน Perceptron
- หลังจากฝึก Perceptron แล้วระบบจะมีชุดถ่วงน้ำหนัก
- ตอนนี้คุณสามารถทดสอบรูปแบบของผู้ใช้ที่ไม่รู้จักและ Perceptron จะสร้างชุดผลลัพธ์
- จัดเก็บการแข่งขันเชิงบวกทั้งหมด
- เรียงลำดับการแข่งขันก่อนตามคะแนนจากนั้นตามความแตกต่าง (ตามที่อธิบายไว้ข้างต้น)
- แสดงผลลัพธ์ที่ใกล้เคียงที่สุดสองรายการหรือหากไม่พบรายการที่ตรงกันให้แสดงผลลัพธ์ที่ว่างเปล่า
รหัสสำหรับการพิสูจน์แนวคิด
$features = array(
'Real IP address' => .5,
'Used proxy IP address' => .4,
'HTTP Cookies' => .9,
'Session Cookies' => .6,
'3rd Party Cookies' => .6,
'Flash Cookies' => .7,
'PDF Bug' => .2,
'Flash Bug' => .2,
'Java Bug' => .2,
'Frequent Pages' => .3,
'Browsers Finger Print' => .3,
'Installed Plugins' => .2,
'URL' => .5,
'Cached PNG' => .4,
'System Fonts Detection' => .6,
'Localstorage' => .8,
'Geolocation' => .6,
'AOLTR' => .4,
'Network Information API' => .3,
'Battery Status API' => .2
);
$labels = array();
$n = 1;
foreach ($features as $k => $v) {
$labels[$k] = "x" . $n;
$n ++;
}
$users = array();
for($i = 0, $name = "A"; $i < 5; $i ++, $name ++) {
$users[] = new Profile($name, $features);
}
$unknown = new Profile("Unknown", $features);
$unknownRNA = array(
0 => array("o" => 1),
1 => array("o" => - 1)
);
foreach ($unknown->data as $item => $point) {
$unknownRNA[0][$labels[$item]] = $point;
$unknownRNA[1][$labels[$item]] = (- 1 * $point);
}
$perceptron = new Perceptron();
$trainResult = $perceptron->train($unknownRNA, 1, 1);
foreach ($users as $name => &$profile) {
$data = array_combine($labels, $profile->data);
if ($perceptron->testCase($data, $trainResult) == true) {
$score = $diff = 0;
foreach ($unknown->data as $item => $found) {
if ($unknown->data[$item] === $profile->data[$item]) {
if ($profile->data[$item] > 0) {
$score += $features[$item];
} else {
$diff += $features[$item];
}
}
}
$profile->setScore($score, $diff);
$matchs[] = $profile;
}
}
if (count($matchs) > 1) {
usort($matchs, function ($a, $b) {
if ($a->score == $b->score) {
return $a->diff == $b->diff ? 0 : ($a->diff > $b->diff ? 1 : - 1);
}
return $a->score > $b->score ? - 1 : 1;
});
echo "<br />Possible Match ", implode(",", array_slice(array_map(function ($v) {
return sprintf(" %s (%0.4f|%0.4f) ", $v->name, $v->score,$v->diff);
}, $matchs), 0, 2));
} else {
echo "<br />No match Found ";
}
เอาท์พุต:
Possible Match D (0.7416|0.16853),C (0.5393|0.2809)
Print_r จาก "D":
echo "<pre>";
print_r($matchs[0]);
Profile Object(
[name] => D
[data] => Array (
[Real IP address] => -1
[Used proxy IP address] => -1
[HTTP Cookies] => 1
[Session Cookies] => 1
[3rd Party Cookies] => 1
[Flash Cookies] => 1
[PDF Bug] => 1
[Flash Bug] => 1
[Java Bug] => -1
[Frequent Pages] => 1
[Browsers Finger Print] => -1
[Installed Plugins] => 1
[URL] => -1
[Cached PNG] => 1
[System Fonts Detection] => 1
[Localstorage] => -1
[Geolocation] => -1
[AOLTR] => 1
[Network Information API] => -1
[Battery Status API] => -1
)
[score] => 0.74157303370787
[diff] => 0.1685393258427
[base] => 8.9
)
หากตรวจแก้จุดบกพร่อง = true คุณจะสามารถที่จะเห็นการป้อนข้อมูล (เซนเซอร์และต้องการอยากมี) น้ำหนักเริ่มต้นขับ (เซนเซอร์, Sum, เครือข่าย), ข้อผิดพลาด, การแก้ไขและน้ำหนักชิงชนะเลิศ
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| o | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15 | x16 | x17 | x18 | x19 | x20 | Bias | Yin | Y | deltaW1 | deltaW2 | deltaW3 | deltaW4 | deltaW5 | deltaW6 | deltaW7 | deltaW8 | deltaW9 | deltaW10 | deltaW11 | deltaW12 | deltaW13 | deltaW14 | deltaW15 | deltaW16 | deltaW17 | deltaW18 | deltaW19 | deltaW20 | W1 | W2 | W3 | W4 | W5 | W6 | W7 | W8 | W9 | W10 | W11 | W12 | W13 | W14 | W15 | W16 | W17 | W18 | W19 | W20 | deltaBias |
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 0 | -1 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
| -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | -1 | -1 | 1 | -19 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
| 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 19 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
| -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | -1 | -1 | 1 | -19 | -1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -1 | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
x1 ถึง x20 แสดงถึงคุณสมบัติที่แปลงโดยรหัส
$labels = array();
$n = 1;
foreach ( $features as $k => $v ) {
$labels[$k] = "x" . $n;
$n ++;
}
นี่คือการสาธิตออนไลน์
คลาสที่ใช้:
class Profile {
public $name, $data = array(), $score, $diff, $base;
function __construct($name, array $importance) {
$values = array(-1, 1);
$this->name = $name;
foreach ($importance as $item => $point) {
$this->data[$item] = $values[mt_rand(0, 1)];
}
$this->base = array_sum($importance);
}
public function setScore($score, $diff) {
$this->score = $score / $this->base;
$this->diff = $diff / $this->base;
}
}
คลาส Perceptron ดัดแปลง
class Perceptron {
private $w = array();
private $dw = array();
public $debug = false;
private function initialize($colums) {
for($i = 1; $i <= $colums; $i ++) {
$this->w[$i] = 0;
$this->dw[$i] = 0;
}
}
function train($input, $alpha, $teta) {
$colums = count($input[0]) - 1;
$weightCache = array_fill(1, $colums, 0);
$checkpoints = array();
$keepTrainning = true;
$this->initialize(count($input[0]) - 1);
$just_started = true;
$totalRun = 0;
$yin = 0;
while ($keepTrainning == true) {
foreach ($input as $row_counter => $row_data) {
$n_columns = count($row_data) - 1;
$yin = 0;
for($i = 1; $i <= $n_columns; $i ++) {
$yin += $row_data["x" . $i] * $weightCache[$i];
}
$Y = ($yin <= 1) ? - 1 : 1;
$checkpoints[$row_counter] = 0;
for($i = 1; $i <= $n_columns; $i ++) {
if ($just_started == true) {
$this->dw[$i] = $weightCache[$i];
$just_started = false;
} elseif ($Y == $row_data["o"]) {
$this->dw[$i] = 0;
} else {
$this->dw[$i] = $row_data["x" . $i] * $row_data["o"];
}
$this->w[$i] = $this->dw[$i] + $weightCache[$i];
$weightCache[$i] = $this->w[$i];
$checkpoints[$row_counter] += $this->w[$i];
}
foreach ($this->w as $index => $w_item) {
$debug_w["W" . $index] = $w_item;
$debug_dw["deltaW" . $index] = $this->dw[$index];
}
$debug_vars[] = array_merge($row_data, array(
"Bias" => 1,
"Yin" => $yin,
"Y" => $Y
), $debug_dw, $debug_w, array(
"deltaBias" => 1
));
}
$empty_data_row = array();
for($i = 1; $i <= $n_columns; $i ++) {
$empty_data_row["x" . $i] = "--";
$empty_data_row["W" . $i] = "--";
$empty_data_row["deltaW" . $i] = "--";
}
$debug_vars[] = array_merge($empty_data_row, array(
"o" => "--",
"Bias" => "--",
"Yin" => "--",
"Y" => "--",
"deltaBias" => "--"
));
$totalRun ++;
$referer_value = end($checkpoints);
$sum = array_sum($checkpoints);
$n_rows = count($checkpoints);
if ($totalRun > 1 && ($sum / $n_rows) == $referer_value) {
$keepTrainning = false;
}
}
$result = array();
for($i = 1; $i <= $n_columns; $i ++) {
$result["w" . $i] = $this->w[$i];
}
$this->debug($this->print_html_table($debug_vars));
return $result;
}
function testCase($input, $results) {
$result = 0;
$i = 1;
foreach ($input as $column_value) {
$result += $results["w" . $i] * $column_value;
$i ++;
}
return ($result > 0) ? true : false;
}
function print_html_table($array) {
$html = "";
$inner_html = "";
$table_header_composed = false;
$table_header = array();
foreach ($array as $array_item) {
$inner_html .= "<tr>\n";
foreach ( $array_item as $array_col_label => $array_col ) {
$inner_html .= "<td>\n";
$inner_html .= $array_col;
$inner_html .= "</td>\n";
if ($table_header_composed == false) {
$table_header[] = $array_col_label;
}
}
$table_header_composed = true;
$inner_html .= "</tr>\n";
}
$html = "<table border=1>\n";
$html .= "<tr>\n";
foreach ($table_header as $table_header_item) {
$html .= "<td>\n";
$html .= "<b>" . $table_header_item . "</b>";
$html .= "</td>\n";
}
$html .= "</tr>\n";
$html .= $inner_html . "</table>";
return $html;
}
function debug($message) {
if ($this->debug == true) {
echo "<b>DEBUG:</b> $message";
}
}
}
สรุป
การระบุผู้ใช้โดยไม่มีตัวระบุเฉพาะไม่ใช่งานตรงไปตรงมาหรือเรียบง่าย ขึ้นอยู่กับการรวบรวมข้อมูลสุ่มในปริมาณที่เพียงพอซึ่งคุณสามารถรวบรวมจากผู้ใช้ด้วยวิธีการต่างๆ
แม้ว่าคุณจะเลือกที่จะไม่ใช้โครงข่ายประสาทเทียม แต่อย่างน้อยฉันขอแนะนำให้ใช้ Simple Probability Matrix ที่มีลำดับความสำคัญและความเป็นไปได้ - และฉันหวังว่าโค้ดและตัวอย่างที่ให้ไว้ข้างต้นจะเพียงพอสำหรับการดำเนินการต่อไป