Algoritmos e programação

Explicação sobre aleatoriedade – Jogo do Ultimato

O Jogo do Ultimato e o seu derivado, o Jogo do Ditador, são dois experimentos clássicos da teoria dos jogos e da economia comportamental que revelam importantes facetas da natureza humana. Ambos os jogos envolvem a divisão de uma quantia em dinheiro entre dois participantes, mas com uma diferença crucial em suas regras, o que leva a resultados e interpretações distintas sobre o que motiva as decisões das pessoas.

O Jogo do Ultimato

No Jogo do Ultimato, dois jogadores, que não se conhecem, têm a tarefa de dividir uma soma de dinheiro. O primeiro jogador, o “Proponente”, recebe a quantia e deve propor uma divisão ao segundo jogador, o “Receptor”. O Receptor, por sua vez, tem duas opções: aceitar a oferta, e nesse caso o dinheiro é dividido conforme a proposta, ou recusá-la, situação em que ambos os jogadores não recebem nada.

A lógica da teoria da escolha racional sugere que o Proponente, buscando maximizar seu próprio ganho, deveria oferecer a menor quantia possível acima de zero (por exemplo, se tiver 10 reais, dar 1 real para o Receptor e ficar com outros 9 reais). O Receptor, agindo de forma puramente racional, deveria aceitar qualquer oferta, por menor que seja, pois qualquer valor é melhor do que zero.

Contudo, os resultados experimentais consistentemente desafiam essa previsão. Na prática, Proponentes geralmente oferecem divisões mais equitativas, frequentemente entre 40% e 50% do total. Ofertas muito baixas, abaixo de 20% a 30%, são frequentemente rejeitadas pelos Receptores.

Essa discrepância evidencia que a tomada de decisão humana não é guiada apenas pelo interesse próprio. Fatores como o senso de justiça e o medo da punição (a rejeição da oferta) desempenham um papel crucial. Os Receptores preferem sair sem nada a aceitar uma oferta que consideram injusta, punindo assim o comportamento egoísta do Proponente. Por sua vez, os Proponentes, antecipando essa possível retaliação, tendem a fazer ofertas mais generosas.

O Jogo do Ditador

O Jogo do Ditador é uma variação simplificada do Jogo do Ultimato. A principal diferença é a eliminação do poder de veto do segundo jogador. No Jogo do Ditador, o primeiro jogador, agora chamado de “Ditador”, decide como dividir a quantia de dinheiro, e o segundo jogador, o “Receptor”, não tem outra opção a não ser aceitar a divisão proposta.

O jogo do ditador não será aproveitado nessa exemplificação, ficando aqui descrita apenas por curiosidade.

Proposta de simulação do jogo do ultimato

Para a simulação, a escolha racional será substituída por uma probabilidade de aceitar a oferta. Se o Proponente oferecer uma divisão de 50%-50%, o Receptor irá sempre aceitar a oferta. Se o Proponente oferecer uma divisão de 100%-0%, o Receptor irá sempre rejeitar a oferta – convenhamos que não faz sentido no jogo o Proponente ofertar essa divisão. As relações intermediárias terão a probabilidade proporcionais no intervalo. Por exemplo, se o Proponente oferecer uma divisão de 60%-40%, o Receptor terá uma probabilidade de 80% em aceitar a oferta.

Para a simulação, vamos considerar que o Proponente tem R$ 100,00 para distribuir. Ele irá sortear uma proposta de valor no intervalo entre 1% e 50% do valor (deixar para o Receptor um valor entre R$ 1,00 e R$ 50,00). O Receptor terá uma probabilidade de 2 vezes a porcentagem oferecida para aceitar a oferta.

Será mostrado ao final a quantidade de ofertas aceitas e recusadas e os valores acumulados do Proponente e do Receptor.

import random

def simular_jogo_do_ultimato(num_simulacoes=1000):
    """
    Argumentos:
        num_simulacoes (int): O número de vezes que a simulação será executada.

    Retorna:
        dict: Um dicionário contendo os resultados da simulação.
    """
    # Contadores para os resultados
    ofertas_aceitas = 0
    ofertas_recusadas = 0
    total_proponente = 0.0
    total_receptor = 0.0
    valor_total_ofertado = 100.0

    print(f"--- Simulação do Jogo do Ultimato ({num_simulacoes} rodadas) ---\n")

    # Loop principal da simulação
    for i in range(num_simulacoes):
        # 1. Proponente sorteia uma oferta entre R$ 1,00 e R$ 50,00
        oferta_proponente = random.randint(1, 50)
        
        # 2. Calcular a probabilidade de aceitação do Receptor
        # A probabilidade é 2x a porcentagem oferecida.
        # Ex: oferta de R$25 (25%) -> prob = 2 * 0.25 = 0.50 (50%)
        percentual_ofertado = oferta_proponente / valor_total_ofertado
        probabilidade_aceitacao = 2 * percentual_ofertado

        # 3. Receptor decide se aceita ou não a oferta
        # Sorteia um número entre 0 e 1. Se for menor ou igual à probabilidade, ele aceita.
        if random.random() <= probabilidade_aceitacao:
            # Oferta ACEITA
            ofertas_aceitas += 1
            ganho_proponente = valor_total_ofertado - oferta_proponente
            ganho_receptor = oferta_proponente
            
            total_proponente += ganho_proponente
            total_receptor += ganho_receptor
        else:
            # Oferta RECUSADA
            ofertas_recusadas += 1
            # Ambos não ganham nada nesta rodada
            ganho_proponente = 0
            ganho_receptor = 0

    # --- Cálculo dos resultados finais ---
    
    # Taxas de aceitação e recusa
    taxa_aceitacao = (ofertas_aceitas / num_simulacoes) * 100
    taxa_recusa = (ofertas_recusadas / num_simulacoes) * 100

    # Ganhos médios por rodada
    ganho_medio_proponente = total_proponente / num_simulacoes
    ganho_medio_receptor = total_receptor / num_simulacoes
    
    # Ganhos médios por rodada bem-sucedida (quando a oferta é aceita)
    ganho_medio_proponente_sucesso = total_proponente / ofertas_aceitas if ofertas_aceitas > 0 else 0
    ganho_medio_receptor_sucesso = total_receptor / ofertas_aceitas if ofertas_aceitas > 0 else 0


    # Monta o dicionário de resultados
    resultados = {
        "Total de Simulações": num_simulacoes,
        "Ofertas Aceitas": ofertas_aceitas,
        "Ofertas Recusadas": ofertas_recusadas,
        "Taxa de Aceitação (%)": f"{taxa_aceitacao:.2f}%",
        "Taxa de Recusa (%)": f"{taxa_recusa:.2f}%",
        "Ganho Total do Proponente": f"R$ {total_proponente:.2f}",
        "Ganho Total do Receptor": f"R$ {total_receptor:.2f}",
        "Ganho Médio por Rodada (Proponente)": f"R$ {ganho_medio_proponente:.2f}",
        "Ganho Médio por Rodada (Receptor)": f"R$ {ganho_medio_receptor:.2f}",
        "Ganho Médio por Oferta Aceita (Proponente)": f"R$ {ganho_medio_proponente_sucesso:.2f}",
        "Ganho Médio por Oferta Aceita (Receptor)": f"R$ {ganho_medio_receptor_sucesso:.2f}",
    }

    return resultados

# Executa a simulação e imprime os resultados
if __name__ == "__main__":
    resultados_finais = simular_jogo_do_ultimato(1000)
    
    for chave, valor in resultados_finais.items():
        print(f"{chave}: {valor}")

Teste o código aqui.

Um exemplo da execução do código é mostrado abaixo. A taxa de recusa deve ficar em torno de 50%, o que fará que em torno de R$ 50.000,00 dos R$ 100.000,00 sejam “embolsados” pelos participantes (algo em torno de R$ 35.000,00 para o Proponente e R$ 15.000,00 para o Receptor).

Total de Simulações: 1000
Ofertas Aceitas: 526
Ofertas Recusadas: 474
Taxa de Aceitação (%): 52.60%
Taxa de Recusa (%): 47.40%
Ganho Total do Proponente: R$ 35295.00
Ganho Total do Receptor: R$ 17305.00
Ganho Médio por Rodada (Proponente): R$ 35.30
Ganho Médio por Rodada (Receptor): R$ 17.30
Ganho Médio por Oferta Aceita (Proponente): R$ 67.10
Ganho Médio por Oferta Aceita (Receptor): R$ 32.90

Experimente agora melhor a distribuição de valores para o Receptor, ou seja, oferecer mais dinheiro para o Receptor. Aumentar o valor dado ao Receptor parece irá diminuir o total arrecadado pelo Proponente. Veja o que acontecer se em veze de propor a divisão entre 1% e 50%, a proporção for entre 25% e 50% (é preciso ajustar a linha 23 para o novo intervalo de valores).

Trago a seguir a versão do código em linguagem C.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    // --- Configuração da Simulação ---
    const int NUM_SIMULACOES = 1000;
    const double VALOR_TOTAL_OFERTADO = 100.0;

    // --- Contadores e Totais ---
    int ofertas_aceitas = 0;
    int ofertas_recusadas = 0;
    double total_proponente = 0.0;
    double total_receptor = 0.0;

    // --- Inicialização do Gerador de Números Aleatórios ---
    // A função 'srand' inicializa o gerador de números aleatórios com uma "semente".
    // Usar 'time(NULL)' como semente garante que a sequência de números aleatórios
    // seja diferente a cada execução do programa. Isso só precisa ser feito uma vez.
    srand(time(NULL));

    printf("--- Iniciando Simulacao do Jogo do Ultimato (%d rodadas) ---\n\n", NUM_SIMULACOES);

    // --- Loop Principal da Simulação ---
    for (int i = 0; i < NUM_SIMULACOES; i++) {
        // 1. Proponente sorteia uma oferta entre 1 e 50
        // rand() % 50 gera um número de 0 a 49. Somamos 1 para obter o intervalo 1-50.
        int oferta_proponente = (rand() % 50) + 1;

        // 2. Calcular a probabilidade de aceitação do Receptor
        double percentual_ofertado = (double)oferta_proponente / VALOR_TOTAL_OFERTADO;
        double probabilidade_aceitacao = 2.0 * percentual_ofertado;

        // 3. Receptor decide se aceita ou não a oferta
        // Geramos um número aleatório de ponto flutuante entre 0.0 e 1.0
        double decisao_aleatoria = (double)rand() / RAND_MAX;

        if (decisao_aleatoria <= probabilidade_aceitacao) {
            // Oferta ACEITA
            ofertas_aceitas++;
            total_proponente += VALOR_TOTAL_OFERTADO - oferta_proponente;
            total_receptor += oferta_proponente;
        } else {
            // Oferta RECUSADA
            ofertas_recusadas++;
            // Ambos não ganham nada nesta rodada
        }
    }

    // --- Cálculo e Exibição dos Resultados Finais ---

    // Taxas de aceitação e recusa
    double taxa_aceitacao = ((double)ofertas_aceitas / NUM_SIMULACOES) * 100.0;
    double taxa_recusa = ((double)ofertas_recusadas / NUM_SIMULACOES) * 100.0;

    // Ganhos médios por rodada
    double ganho_medio_proponente = total_proponente / NUM_SIMULACOES;
    double ganho_medio_receptor = total_receptor / NUM_SIMULACOES;
    
    // Ganhos médios por rodada bem-sucedida (quando a oferta é aceita)
    double ganho_medio_proponente_sucesso = (ofertas_aceitas > 0) ? total_proponente / ofertas_aceitas : 0;
    double ganho_medio_receptor_sucesso = (ofertas_aceitas > 0) ? total_receptor / ofertas_aceitas : 0;


    printf("--- Resultados Finais ---\n");
    printf("Total de Simulacoes: %d\n", NUM_SIMULACOES);
    printf("Ofertas Aceitas: %d\n", ofertas_aceitas);
    printf("Ofertas Recusadas: %d\n", ofertas_recusadas);
    printf("Taxa de Aceitacao: %.2f%%\n", taxa_aceitacao); // %% para imprimir o caractere '%'
    printf("Taxa de Recusa: %.2f%%\n", taxa_recusa);
    printf("----------------------------------------\n");
    printf("Ganho Total do Proponente: R$ %.2f\n", total_proponente);
    printf("Ganho Total do Receptor: R$ %.2f\n", total_receptor);
    printf("----------------------------------------\n");
    printf("Ganho Medio por Rodada (Proponente): R$ %.2f\n", ganho_medio_proponente);
    printf("Ganho Medio por Rodada (Receptor): R$ %.2f\n", ganho_medio_receptor);
    printf("Ganho Medio por Oferta Aceita (Proponente): R$ %.2f\n", ganho_medio_proponente_sucesso);
    printf("Ganho Medio por Oferta Aceita (Receptor): R$ %.2f\n", ganho_medio_receptor_sucesso);
    printf("\n--- Fim da Simulacao ---\n");

    return 0; // Indica que o programa terminou com sucesso
}

Sair da versão mobile