Há quem ache — e eu concordo — que a UEFA Champions League é um torneio mais relevante do que a Copa do Mundo, mas não podemos negar que o Mundial mexe com a nossa rotina. Mesmo quem não é apaixonado pelo esporte passa a prestar atenção nas seleções, nas partidas, na festa.
E com a Copa do Mundo vem os bolões. Muitos bolões.
Todo mundo já sabe como funciona um bolão, embora a mecânica possa mudar de um lugar para outro:
- Cada palpite tem um valor fixo (ex: R$2,00).
- Cada um pode dar quantos palpites quiser.
- No final, o montante arrecadado é dividido igualmente entre os vencedores. Se não houverem vencedores: (a) o bolão “acumula”1, ou (b) todo mundo recebe seu dinheiro de volta, ou (c) sua-regra-maluca-aqui.
Esse tipo de bolão é interessante, mas ele podia ser melhor em alguns aspectos.
Primeiro, o organizador acaba perdendo muito tempo correndo atrás de troco, afinal não é todo mundo que anda com dinheiro trocado na carteira. Além disso, valor fixo de palpite é muito chato, pois o valor arrecadado é dividido por igual entre os ganhadores. O bolão fica muito mais interessante quando o valor do palpite é variável e o valor arrecadado é dividido proporcionalmente entre os ganhadores. Assim, no caso de vários ganhadores, ganha mais quem apostar mais.
O problema de se fazer bolões com palpite variável é ter que ficar calculando o valor proporcional a ser dado a cada ganhador. Portanto eis o desafio: criar um programa que receba uma lista com todos os palpites e retorne uma lista com os palpites vencedores juntamente com o valor proporcional a ser pago a cada ganhador.
E aí, topa?
Resolvendo o problema… em Clojure
Como escrever mais código do que necessário está saindo de moda, optei por codificar uma possível solução em Clojure. Por ser uma linguagem funcional, Clojure é excelente para esse tipo de tarefa. Basta iniciar o REPL e mãos à obra!
Primeiramente, devemos definir como um palpite deve ser representado:
(defstruct bet :name :amount :guess :rate)
Todos os palpites possuem o mesmo conjunto de informações, por isso definimos um StructMap com as chaves :name, :amount, :guess e :rate.
Abaixo, uma possível lista de palpites:
(def pool [(struct bet "Daniel" 2.0 [2 0]) ;; Daniel aposta R$2,00 no placar 2x0 (struct bet "Antônio" 4.0 [0 0]) ;; Antônio aposta R$4,00 no placar 0x0 (struct bet "Marcela" 1.0 [2 0]) ;; Marcela aposta R$1,00 no placar 2x0 ])
Certo, já sabemos como os palpites são representados. Agora, como saber o montante total de uma lista de palpites (a.k.a. bolão)? Simples, basta calcular o somatório dos valores de todos os palpites:
(defn pool-amount [poolseq] (reduce + (map :amount poolseq))) ;; testando (pool-amount pool) => 7.0
Beleza, mas quanto cada palpite representa em relação ao total arrecadado? Afinal, precisamos calcular o valor proporcional a ser pago a cada ganhador.
Sem problemas, basta dividir o valor de cada palpite pelo montante total:
(defn pool-rates [poolseq] (let [sum (pool-amount poolseq)] (map #(assoc % :rate (/ (:amount %) sum)) poolseq))) ;; testando (map :rate (pool-rates pool)) => (0.285 ..., 0.571 ..., 0.142 ...) (reduce + (map :rate (pool-rates pool))) => 1.0
Repare que, se todos os participantes ganhassem um bolão, o valor recebido pelo Antônio seria o dobro do valor recebido pelo Daniel, que seria o dobro do valor recebido pela Marcela. Justiça, finalmente!
Pronto, agora só falta uma função para filtrar os ganhadores da lista de participantes e calcular o valor proporcional a ser pago a cada um deles:
(defn pool-winners [poolseq result] (let [sum (pool-amount poolseq) winners (filter #(= (:guess %) result) poolseq)] (map #(assoc % :amount (double (* (:rate %) sum))) (pool-rates winners)))) ;; testando (pool-winners pool [1 0]) ;; sem ganhadores => () (pool-winners pool [0 0]) ;; apenas um ganhador => ({:name "Antônio", :amount 7.0, :guess [0 0], :rate 1.0}) (pool-winners pool [2 0]) ;; vários ganhadores => ({:name "Daniel", :amount 4.66 ..., :guess [2 0], :rate 0.66 ...} {:name "Marcela", :amount 2.33 ..., :guess [2 0], :rate 0.33 ...})
Uma grande vantagem dessa solução é que ela permite que você aposte em qualquer coisa. Quer apostar no sexo de um bebê logo após a boa notícia (ou má notícia, sei lá)?
;; :male = masculino, :female = feminino (def pool [(struct bet "Daniel" 3.0 :male) (struct bet "Antônio" 5.0 :female) (struct bet "Fran" 1.0 :female)]) (pool-winners pool :female) ;; testando => ({:name "Antônio", :amount 7.5, ...} {:name "Fran", :amount 1.5, ...})
Aí está, uma solução simples para um problema simples. Como deveria ser sempre.
Minha solução, na íntegra
Embora seja possível resolver isso com um one-liner em Lisp/Clojure (afinal o Reader não trabalha com linhas, mas com estruturas de dados), eu optei por escrever um código que fosse o mais legível possível para quem não conhece ou está aprendendo Clojure:
(defstruct bet :name :amount :guess :rate) (defn pool-amount [poolseq] (reduce + (map :amount poolseq))) (defn pool-rates [poolseq] (let [sum (pool-amount poolseq)] (map #(assoc % :rate (/ (:amount %) sum)) poolseq))) (defn pool-winners [poolseq result] (let [sum (pool-amount poolseq) winners (filter #(= (:guess %) result) poolseq)] (map #(assoc % :amount (double (* (:rate %) sum))) (pool-rates winners))))
Levando adiante
Você programa em Haskell? Python? Ruby? Brainfuck? Qual seria a sua solução para este problema de extrema importância para o desenvolvimento da raça humana?
Atualização (03/11/2010). Solução em Common Lisp, usando copy-* para evitar mutabilidade:
(defstruct bet name amount guess rate) (defun pool-amount (pool) (reduce #'+ (mapcar #'bet-amount pool))) (defun pool-rates (pool) (let ((sum (pool-amount pool))) (mapcar (lambda (bet) (let ((x (copy-bet bet))) (setf (bet-rate x) (/ (bet-amount x) sum)) x)) pool))) (defun pool-winners (pool result) (let ((sum (pool-amount pool)) (winners (loop for bet in pool when (equal (bet-guess bet) result) collect bet))) (mapcar (lambda (winner) (let ((x (copy-bet winner))) (setf (bet-amount x) (* (bet-rate x) sum)) x)) (pool-rates winners))))
- Todo o montante recebido continua disponível no próximo bolão, mas os participantes devem pagar um novo palpite. ↩
Posts em Português
Posts in English