Update: 2026-07-02 05:27:04
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
/**
|
||||
* ai_formula_solver.php
|
||||
* مكتشف خوارزميات المنافسين (AI Competitor Formula Solver)
|
||||
* يستخدم الانحدار الخطي المتعدد (Multiple Linear Regression) لاكتشاف
|
||||
* أجرة فتح العداد، وسعر الكيلومتر، وسعر الدقيقة لكل تطبيق منافس.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../core/bootstrap.php';
|
||||
require_once __DIR__ . '/../functions.php';
|
||||
|
||||
try {
|
||||
$con = Database::get('main');
|
||||
} catch (Exception $e) {
|
||||
die("Database connection failed: " . $e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
echo "Starting AI Formula Discovery Engine...\n";
|
||||
|
||||
// نجلب التطبيقات التي لديها بيانات (مسافة ووقت وسعر)
|
||||
$sqlApps = "SELECT DISTINCT competitor_name, country_code
|
||||
FROM scraped_competitor_prices
|
||||
WHERE distance_km > 0 AND duration_min > 0 AND price_amount > 0";
|
||||
$stmtApps = $con->query($sqlApps);
|
||||
$apps = $stmtApps->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($apps)) {
|
||||
echo "No sufficient data (Distance/Duration) found to perform regression.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
// دالة لحل نظام معادلات خطية (Gaussian Elimination)
|
||||
function solveLinearSystem($A, $B) {
|
||||
$n = count($A);
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
// Search for maximum in this column
|
||||
$maxEl = abs($A[$i][$i]);
|
||||
$maxRow = $i;
|
||||
for ($k = $i + 1; $k < $n; $k++) {
|
||||
if (abs($A[$k][$i]) > $maxEl) {
|
||||
$maxEl = abs($A[$k][$i]);
|
||||
$maxRow = $k;
|
||||
}
|
||||
}
|
||||
|
||||
// Swap maximum row with current row
|
||||
for ($k = $i; $k < $n; $k++) {
|
||||
$tmp = $A[$maxRow][$k];
|
||||
$A[$maxRow][$k] = $A[$i][$k];
|
||||
$A[$i][$k] = $tmp;
|
||||
}
|
||||
$tmp = $B[$maxRow];
|
||||
$B[$maxRow] = $B[$i];
|
||||
$B[$i] = $tmp;
|
||||
|
||||
// Make all rows below this one 0 in current column
|
||||
for ($k = $i + 1; $k < $n; $k++) {
|
||||
if ($A[$i][$i] == 0) continue;
|
||||
$c = -$A[$k][$i] / $A[$i][$i];
|
||||
for ($j = $i; $j < $n; $j++) {
|
||||
if ($i == $j) {
|
||||
$A[$k][$j] = 0;
|
||||
} else {
|
||||
$A[$k][$j] += $c * $A[$i][$j];
|
||||
}
|
||||
}
|
||||
$B[$k] += $c * $B[$i];
|
||||
}
|
||||
}
|
||||
|
||||
// Solve equation Ax=b for an upper triangular matrix A
|
||||
$x = array_fill(0, $n, 0);
|
||||
for ($i = $n - 1; $i >= 0; $i--) {
|
||||
if ($A[$i][$i] == 0) continue;
|
||||
$x[$i] = $B[$i] / $A[$i][$i];
|
||||
for ($k = $i - 1; $k >= 0; $k--) {
|
||||
$B[$k] -= $A[$k][$i] * $x[$i];
|
||||
}
|
||||
}
|
||||
return $x;
|
||||
}
|
||||
|
||||
foreach ($apps as $app) {
|
||||
$competitor = $app['competitor_name'];
|
||||
$countryCode = $app['country_code'];
|
||||
|
||||
echo "Analyzing: $competitor ($countryCode)...\n";
|
||||
|
||||
// سحب أحدث 5000 رحلة لتكوين نموذج رياضي دقيق
|
||||
$sqlData = "SELECT distance_km, duration_min, price_amount
|
||||
FROM scraped_competitor_prices
|
||||
WHERE competitor_name = :comp
|
||||
AND country_code = :country
|
||||
AND distance_km > 0 AND duration_min > 0 AND price_amount > 0
|
||||
ORDER BY id DESC LIMIT 5000";
|
||||
$stmtData = $con->prepare($sqlData);
|
||||
$stmtData->execute([':comp' => $competitor, ':country' => $countryCode]);
|
||||
$samples = $stmtData->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$N = count($samples);
|
||||
if ($N < 10) {
|
||||
echo " -> Not enough samples ($N). Skipping.\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
// بناء مصفوفات Least Squares (X^T X) * Beta = (X^T Y)
|
||||
// Beta = [Base_Fare, KM_Price, Min_Price]
|
||||
$sum_x1 = 0; $sum_x2 = 0; $sum_y = 0;
|
||||
$sum_x1_sq = 0; $sum_x2_sq = 0; $sum_x1_x2 = 0;
|
||||
$sum_x1_y = 0; $sum_x2_y = 0;
|
||||
|
||||
foreach ($samples as $s) {
|
||||
$x1 = (float)$s['distance_km'];
|
||||
$x2 = (float)$s['duration_min'];
|
||||
$y = (float)$s['price_amount'];
|
||||
|
||||
$sum_x1 += $x1;
|
||||
$sum_x2 += $x2;
|
||||
$sum_y += $y;
|
||||
|
||||
$sum_x1_sq += ($x1 * $x1);
|
||||
$sum_x2_sq += ($x2 * $x2);
|
||||
$sum_x1_x2 += ($x1 * $x2);
|
||||
|
||||
$sum_x1_y += ($x1 * $y);
|
||||
$sum_x2_y += ($x2 * $y);
|
||||
}
|
||||
|
||||
$matrixA = [
|
||||
[$N, $sum_x1, $sum_x2],
|
||||
[$sum_x1, $sum_x1_sq, $sum_x1_x2],
|
||||
[$sum_x2, $sum_x1_x2, $sum_x2_sq]
|
||||
];
|
||||
|
||||
$matrixB = [
|
||||
$sum_y,
|
||||
$sum_x1_y,
|
||||
$sum_x2_y
|
||||
];
|
||||
|
||||
// حل المصفوفة
|
||||
try {
|
||||
$beta = solveLinearSystem($matrixA, $matrixB);
|
||||
|
||||
$baseFare = round(max(0, $beta[0]), 3); // Base fare cannot be negative
|
||||
$kmPrice = round(max(0, $beta[1]), 3);
|
||||
$minPrice = round(max(0, $beta[2]), 3);
|
||||
|
||||
echo " -> [DISCOVERED] Base Fare: $baseFare, KM: $kmPrice, Min: $minPrice\n";
|
||||
|
||||
// حفظ في جدول المعادلات السرية
|
||||
$sqlUpsert = "INSERT INTO competitor_secret_formulas
|
||||
(competitor_name, country_code, base_fare, price_per_km, price_per_min, sample_size)
|
||||
VALUES (:comp, :country, :base, :km, :min, :size)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
base_fare = :base, price_per_km = :km, price_per_min = :min, sample_size = :size, last_updated = NOW()";
|
||||
|
||||
$stmtUp = $con->prepare($sqlUpsert);
|
||||
$stmtUp->execute([
|
||||
':comp' => $competitor,
|
||||
':country' => $countryCode,
|
||||
':base' => $baseFare,
|
||||
':km' => $kmPrice,
|
||||
':min' => $minPrice,
|
||||
':size' => $N
|
||||
]);
|
||||
|
||||
echo " -> Saved successfully.\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo " -> Error solving matrix: " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
?>
|
||||
Reference in New Issue
Block a user