diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..830d6a8 --- /dev/null +++ b/CMakeLists.txt @@ -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}) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..eecf3cb --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,392 @@ +#include "iostream" +#include "array" +#include +#include +#include +#include +#include +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 countVotePerCand; + unsigned int totalVotes = 0; + unsigned int totalAbs = 0; +}; + +std::vector candidats; +std::vector 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> 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> 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 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> 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; +} \ No newline at end of file