Initial commit
This commit is contained in:
@@ -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
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user