Initial commit

This commit is contained in:
Tom Ray
2026-05-29 23:50:05 +02:00
parent 79f201577a
commit 650467054e
2 changed files with 401 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.10.0)
project(SimulateurFranceVote VERSION 0.1.0 LANGUAGES C CXX)
file(GLOB_RECURSE
SRCS
src/*
)
add_executable(SimulateurFranceVote ${SRCS})
+392
View File
@@ -0,0 +1,392 @@
#include "iostream"
#include "array"
#include <algorithm>
#include <map>
#include <random>
#include <unordered_map>
#include <vector>
constexpr size_t VotantsInscrits = 49339714;
std::mt19937 rng(std::random_device{}());
// Nouveau : Géographie
/*enum class Region {
// Grandes régions culturelles/politiques
ILE_DE_FRANCE, // Métropole parisienne
BASSIN_PARISIEN, // Picardie, Centre, Normandie
GRAND_EST, // Alsace, Lorraine, Champagne
HAUTS_DE_FRANCE, // Nord - ancien bassin minier
NORMANDIE,
BRETAGNE,
PAYS_DE_LOIRE,
CENTRE_VAL_LOIRE,
NOUVELLE_AQUITAINE,
OCCITANIE,
PROVENCE_ALPES_COTE_AZUR,
AUVERGNE_RHONE_ALPES,
BOURGOGNE_FRANCHE_COMTE,
CORSE,
DOM_TOM
};*/
// Ou version plus simple : typologie urbaine
enum class TypeZone {
METROPOLE_GRANDE, // Paris, Lyon, Marseille, Lille, Bordeaux
METROPOLE_MOYENNE, // 100k-500k
VILLE_MOYENNE, // 20k-100k
VILLE_PETITE, // 2k-20k
RURAL_PROFOND, // <2k
PERIURBAIN, // Couronne des métropoles
BANLIEUE_SENSIBLE // Quartiers politique de la ville + défavorisée
};
//Candidat
enum class Bord
{
EXTREME_GAUCHE,
GAUCHE,
CENTRE_GAUCHE,
CENTRE_DROIT,
DROITE,
EXTREME_DROITE
};
struct Candidat
{
std::string name;
Bord bord;
double position_economique; // -1 = collectivisme, +1 = libéralisme
double position_societale; // -1 = progressiste, +1 = conservateur
double position_europe; // -1 = fédéraliste, +1 = eurosceptique
};
enum class TrancheAge
{
MOINS_25, // 18-24 ans
_25_34, // 25-34 ans
_35_49, // 35-49 ans
_50_64, // 50-64 ans
_65_79, // 65-79 ans
_80_PLUS // 80 ans et plus
};
//Population
enum class CategorieSocioPro
{
Agriculteurs,
ArtisansCommerçants,
CadresCadresSup,
ProfessionsIntermediaires,
EmployesQualifies,
EmployesNonQualifies,
OuvriersQualifies,
OuvriersNonQualifies,
JamaisTravaille
};
enum class NiveauDiplome
{
AUCUN,
BREVET,
BAC,
BACPLUS
};
// Structure pour une strate de population
struct Strate {
CategorieSocioPro csp;
TrancheAge age;
NiveauDiplome diplome;
TypeZone zone;
//Region region;
double population; // nombre d'individus
double probabiliteParticipation;
// Position politique moyenne de la strate
double position_economique;
double position_societale;
double position_europe;
};
struct Result {
std::unordered_map<std::string, unsigned int> countVotePerCand;
unsigned int totalVotes = 0;
unsigned int totalAbs = 0;
};
std::vector<Candidat> candidats;
std::vector<Strate> strates;
// Calcule la distance politique entre un électeur (strate) et un candidat
double distancePolitique(const Strate& strate, const Candidat& candidat) {
double diff_economic = strate.position_economique - candidat.position_economique;
double diff_societal = strate.position_societale - candidat.position_societale;
double diff_europe = strate.position_europe - candidat.position_europe;
// Pondération : le sociétal et l'europe sont plus clivants qu'avant
return std::sqrt(
0.4 * diff_economic * diff_economic +
0.4 * diff_societal * diff_societal +
0.1 * diff_europe * diff_europe
);
}
// Détermine la position politique d'une strate selon ses caractéristiques
void calculerPositionStrate(Strate& strate) {
double eco = 0.0, soc = 0.0, eu = 0.0;
// Effet CSP
switch(strate.csp) {
case CategorieSocioPro::Agriculteurs:
eco = -0.2; soc = 0.5; eu = 0.6; break;
case CategorieSocioPro::OuvriersNonQualifies:
eco = -0.4; soc = 0.4; eu = 0.5; break;
case CategorieSocioPro::CadresCadresSup:
eco = 0.4; soc = -0.3; eu = -0.1; break;
case CategorieSocioPro::EmployesQualifies:
eco = 0.0; soc = 0.1; eu = 0.2; break;
default:
eco = 0.0; soc = 0.0; eu = 0.0;
}
// Effet âge
switch(strate.age) {
case TrancheAge::MOINS_25:
soc -= 0.3; eco -= 0.2; eu -= 0.2; break;
case TrancheAge::_65_79:
soc += 0.2; break;
case TrancheAge::_80_PLUS:
soc += 0.3; eco += 0.1; break;
default: break;
}
// Effet diplôme
switch(strate.diplome) {
case NiveauDiplome::BACPLUS:
eco += 0.2; soc -= 0.2; eu -= 0.2; break;
case NiveauDiplome::AUCUN:
eco -= 0.1; soc += 0.3; eu += 0.3; break;
default: break;
}
// Effet géographique
switch(strate.zone) {
case TypeZone::METROPOLE_GRANDE:
eco += 0.3; soc -= 0.2; eu -= 0.1; break;
case TypeZone::RURAL_PROFOND:
eco -= 0.2; soc += 0.4; eu += 0.3; break;
case TypeZone::BANLIEUE_SENSIBLE:
eco -= 0.3; soc += 0.1; eu -= 0.1; break;
default: break;
}
switch(strate.region) {
case Region::PROVENCE_ALPES_COTE_AZUR:
soc += 0.2; eu += 0.2; break;
case Region::ILE_DE_FRANCE:
eco += 0.2; soc -= 0.1; eu += 0.1; break;
default: break;
}
// Clamping entre -1 et 1
strate.position_economique = std::max(-1.0, std::min(1.0, eco));
strate.position_societale = std::max(-1.0, std::min(1.0, soc));
strate.position_europe = std::max(-1.0, std::min(1.0, eu));
}
// Vote avec un modèle probabiliste
std::string voter(const Strate& strate, std::mt19937& gen) {
// Calcule les distances à tous les candidats
std::vector<std::pair<std::string, double>> scores;
for (const auto& candidat : candidats) {
double distance = distancePolitique(strate, candidat);
// La probabilité est inversement proportionnelle à la distance
// avec un facteur de bruit (température)
double temperature = 0.05; // Plus c'est grand, plus le vote est aléatoire
double score = std::exp(-distance / temperature);
scores.push_back({candidat.name, score});
}
// Tirage aléatoire pondéré par les scores
std::uniform_real_distribution<> dis(0, 1);
double r = dis(gen);
double cumul = 0.0;
for (const auto& [nom, score] : scores) {
cumul += score;
if (r < cumul) return nom;
}
return scores.back().first;
}
void initStrates()
{
struct sousStrate{
TypeZone zone;
TrancheAge age;
CategorieSocioPro csp;
double pop;
};
std::vector<std::tuple<CategorieSocioPro, TrancheAge, NiveauDiplome, TypeZone, double>> rawData = {
// Format : CSP, Age, Diplome, Zone, Region, population (en millions)
//Ile de france
{CategorieSocioPro::CadresCadresSup, TrancheAge::MOINS_25, NiveauDiplome::BACPLUS,
TypeZone::METROPOLE_GRANDE, 2.3},
{CategorieSocioPro::CadresCadresSup, TrancheAge::MOINS_25, NiveauDiplome::BACPLUS,
TypeZone::METROPOLE_GRANDE, 2.3},
{CategorieSocioPro::CadresCadresSup, TrancheAge::MOINS_25, NiveauDiplome::BACPLUS,
TypeZone::METROPOLE_GRANDE, 2.3},
};
double total = 0;
for (const auto& [csp, age, diplome, zone, pop] : rawData) {
Strate s;
s.csp = csp;
s.age = age;
s.diplome = diplome;
s.zone = zone;
//s.region = region;
s.population = pop * 1e6; // Convertir en individus
total += s.population;
// Taux de participation selon âge et CSP
switch (age) {
case TrancheAge::MOINS_25: s.probabiliteParticipation = 0.58;
case TrancheAge::_25_34: s.probabiliteParticipation = 0.54;
case TrancheAge::_35_49: s.probabiliteParticipation = 0.78;
case TrancheAge::_50_64: s.probabiliteParticipation = 0.84;
case TrancheAge::_65_79: s.probabiliteParticipation = 0.88;
case TrancheAge::_80_PLUS: s.probabiliteParticipation = 0.77;
break;
}
calculerPositionStrate(s);
strates.push_back(s);
}
// Normaliser pour atteindre exactement VotantsInscrits
double facteur = (double)VotantsInscrits / total;
for (auto& s : strates) {
s.population *= facteur;
}
}
void simulateElection()
{
Result result;
// Initialiser les compteurs pour chaque candidat
for (const auto& c : candidats) {
result.countVotePerCand[c.name] = 0;
}
std::cout << "=== SIMULATION ÉLECTION PRÉSIDENTIELLE ===\n";
std::cout << "Population totale: " << VotantsInscrits << " inscrits\n\n";
int total_votants_simules = 0;
// Pour chaque strate
for (const auto& strate : strates) {
// Nombre de votants dans cette strate (binomial)
std::binomial_distribution<int> dist_votants(strate.population,
strate.probabiliteParticipation);
int votants = dist_votants(rng);
int abstention = (int)strate.population - votants;
result.totalAbs += abstention;
// Faire voter chaque électeur
for (int i = 0; i < votants; ++i) {
std::string vote = voter(strate, rng);
result.countVotePerCand[vote]++;
result.totalVotes++;
}
// Debug : affichage par strate
std::cout << "Strate: " << (int)strate.csp << "/" << (int)strate.age
<< " - Pop: " << (int)strate.population
<< " - Votants: " << votants << "\n";
}
// Affichage des résultats
std::cout << "\n=== RÉSULTATS ===\n";
std::cout << "Total votants: " << result.totalVotes << "\n";
std::cout << "Total abstentions: " << result.totalAbs << "\n";
std::cout << "Taux participation: "
<< (100.0 * result.totalVotes / VotantsInscrits) << "%\n\n";
// Trier les candidats par nombre de voix
std::vector<std::pair<std::string, unsigned int>> sorted;
for (const auto& [nom, voix] : result.countVotePerCand) {
sorted.push_back({nom, voix});
}
std::sort(sorted.begin(), sorted.end(),
[](auto& a, auto& b) { return a.second > b.second; });
std::cout << "Scores du premier tour:\n";
for (const auto& [nom, voix] : sorted) {
double pct = 100.0 * voix / result.totalVotes;
std::cout << " " << nom << ": " << voix << " voix (" << pct << "%)\n";
}
// Simulation second tour (top 2)
std::string qualif1 = sorted[0].first;
std::string qualif2 = sorted[1].first;
std::cout << "\n=== SECOND TOUR ===\n";
std::cout << qualif1 << " vs " << qualif2 << "\n\n";
// Re-simuler le second tour avec reports de voix
//simulerSecondTour(qualif1, qualif2, result);
}
void initCandidats()
{
candidats = {
// Extrême gauche
{"L'Arthaud", Bord::EXTREME_GAUCHE, -0.8, -0.7, -0.3},
{"Poutou", Bord::EXTREME_GAUCHE, -0.9, -0.6, -0.4},
// Gauche
{"Mélenchon", Bord::GAUCHE, -0.6, -0.4, -0.2},
{"Ruffin", Bord::GAUCHE, -0.5, -0.3, -0.1},
// Centre gauche
{"Glucksmann", Bord::CENTRE_GAUCHE, -0.2, -0.3, 0.1},
{"Rousseau", Bord::CENTRE_GAUCHE, -0.1, -0.2, 0.0},
// Centre droit
{"Macron", Bord::CENTRE_DROIT, 0.3, 0.0, 0.4},
{"Philippe", Bord::CENTRE_DROIT, 0.2, 0.1, 0.3},
// Droite
{"Wauquiez", Bord::DROITE, 0.4, 0.5, 0.2},
{"Bertrand", Bord::DROITE, 0.3, 0.4, 0.1},
// Extrême droite
{"Le Pen", Bord::EXTREME_DROITE, 0.2, 0.7, 0.8},
{"Zemmour", Bord::EXTREME_DROITE, 0.5, 0.9, 0.9}
};
}
int main(int argc, char** argv)
{
initCandidats();
initStrates();
simulateElection();
return 0;
}