Statistiques d'interrogation de l'ingénierie inverse

22

introduction

Étant donné un ensemble de pourcentages de choix dans un sondage, calculez le nombre minimum d'électeurs qu'il doit y avoir dans le sondage pour générer ces statistiques.

Exemple: Quel est votre animal préféré?

  • Chien: 44.4%
  • Chat: 44.4%
  • Souris: 11.1%

Sortie: 9(nombre minimum d'électeurs possible)

Spécifications

Voici les exigences de votre programme / fonction:

  • Vous disposez d'un tableau de valeurs de pourcentage en entrée (sur stdin, comme argument de fonction, etc.)
  • Chaque pourcentage est un nombre arrondi à une décimale (par exemple, 44.4 44.4 11.1).
  • Calculez le nombre minimum possible d'électeurs dans le sondage dont les résultats donneraient ces pourcentages exacts lorsqu'ils sont arrondis à une décimale (sur stdout ou valeur de retour de fonction).
  • Bonus : -15 caractères si vous pouvez résoudre de manière "non triviale" (c'est-à-dire, n'implique pas d'itérer sur tous les # d'électeurs possibles jusqu'à ce que vous trouviez le premier qui fonctionne)

Exemple

>./pollreverse 44.4 44.4 11.1
9
>./pollreverse 26.7 53.3 20.0
15
>./pollreverse 48.4 13.7 21.6 6.5 9.8
153
>./pollreverse 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 99.6
2000
>./pollreverse 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 98.7
667
>./pollreverse 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 98.7
2000
>./pollreverse 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 97.8
401

Notation

Il s'agit de code-golf, donc les personnages les plus courts possibles gagnent. Tous les bonus sont encore soustraits du nombre total de personnages.

mellamokb
la source
2
Je pense que cela pourrait faire avec quelques cas plus difficiles à tester. 26.7 53.3 20.0(4 8 3 de 15), 48.4 13.7 21.6 6.5 9.8(74 21 33 10 15 de 153) etc.
Gareth
@Gareth: Bonne pensée. Mis à jour avec vos cas de test.
mellamokb
la somme de tous les votes ne devrait-elle pas être de 100%? ce n'est pas dans les quatre derniers tests
Ali1S232
@Gajet: Non, ce n'est pas toujours égal à 100%. Chaque fois qu'il y a un arrondi vers le bas, vous perdez jusqu'à 0.5%du total, et chaque fois qu'il y a un arrondi vers le haut, vous ajoutez 0.5%au total. Les quatre derniers cas de test ont été délibérément construits pour exploiter de manière optimale ce phénomène. Dans le premier cas de test qui en résulte 2000, chacune des 9 premières entrées représente le 1vote (et sont toutes arrondies 0.5%), tandis que la dernière représente les 1991votes (et est arrondie vers le bas ~ 0.5%). Si vous calculez ces pourcentages manuellement et arrondissez à 1 décimale, vous verrez qu'ils sont tous corrects.
mellamokb
Je me bats avec la réponse non triviale dans VBA (essayant depuis jusqu'à présent, il n'y en a pas eu), mais j'y travaille!
Gaffi

Réponses:

2

APL (Dyalog Classic) , 48 43 octets

-5 octets par Adám

+/0(⊢+{(⌈/⍷⊢)⍺-⍵÷+/⍵})⍣{z≡⍎3⍕⍺÷+/⍺}⍨z←.01×⎕

Programme complet en prenant l'entrée de stdin.

Essayez-le en ligne! Le lien est vers la version dfn.

Non golfé

normalize   ÷ +/
find_max  {⍵⍷⍨⌈/⍵}
round  {⍎3⍕⍵}
increase  {find_max  - normalize ⍵}
vote_totals  {z←⍺   (⊢+increase)⍣{z  round normalize ⍺} ⍵}
h  {+/ (.01×⍵) vote_totals 0}

Essayez-le en ligne!

  • normalizedivise ( ÷) tous les éléments de son argument de droite ( ) par sa somme ( +/).
  • round(y)arrondit y à 3 décimales en formatant ( ) puis en évaluant ( ) chaque élément de y.
  • find_max(y) renvoie un tableau avec 1 où max (y) est trouvé et 0 ailleurs.
  • increase(x,y) prend x (les pourcentages des objectifs) et y (le tableau des totaux de vote actuels) et calcule où ajouter 1 en y pour rapprocher les pourcentages de x.
  • vote_totals(x,y) prend x (les pourcentages d'objectif) et y (le total des votes de départ) et exécute f à plusieurs reprises, en ajoutant des votes jusqu'à ce que les pourcentages soient arrondis à x.
    • La syntaxe f ⍣ gsignifie d'exécuter à fplusieurs reprises jusqu'à ce que g(y,f(y))soit vrai. Dans ce cas, nous ignorons f(y).
  • h(x) met y à 0 (équivalent à un tableau de 0 en raison de la vectorisation), exécute g et additionne le total des votes finaux.
lirtosiast
la source
7

Python, 154

def p(x):
 n=[1]*len(x);d=2;r=lambda z:round(1000.*z/d)/10
 while 1:
    if(map(r,n),sum(n))==(x,d):return d
    d+=1
    for i in range(len(x)):n[i]+=r(n[i])<x[i]

Cela fonctionne pour le dernier exemple maintenant.

L'exemple s'exécute:

>>> p([44.4, 44.4, 11.1])
9
>>> p([26.7, 53.3, 20.0])
15
>>> p([48.4, 13.7, 21.6, 6.5, 9.8])
153
>>> p([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 99.6])
2000
>>> p([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 98.7])
667
>>> p([0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 98.7])
2000
>>> p([0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 97.8])
401
grc
la source
Je pense que quelque chose ne va pas dans votre dernier exemple; peut-être que vous vouliez dire 99.1comme la dernière valeur
Cristian Lupascu
2
Je pense que c'est vrai, mais c'est assez déroutant. 1/2000 = 0.05%( 0.1%arrondi) et 1991/2000 = 99.55%( 99.6%arrondi). Donc, s'il y a dix options dans un sondage et que neuf d'entre elles sont votées pour une fois tandis que la dernière obtient 1991, alors cela donnerait ces pourcentages.
grc
Tu as raison. Excellente solution, BTW.
Cristian Lupascu
Je pense que vous pouvez enregistrer 3 caractères supplémentaires en suivant cette astuce: codegolf.stackexchange.com/a/58/3527
Cristian Lupascu
Merci, w0lf. Je l'ai mis à jour maintenant pour inclure des onglets. Les onglets apparaissent comme quatre espaces si quelqu'un se pose la question.
grc
4

J, 57 caractères

t=:".>'1'8!:0|:100*%/~i.1001
{.I.*/"1(t{~i.#t)e."1~1!:1[1

J'ai utilisé la méthode triviale. Il prend l'entrée du clavier. tcrée une table de recherche et la deuxième ligne recherche l'entrée dans la table. Je peux fournir une explication détaillée du code si quelqu'un est intéressé.

J'avais cherché à utiliser le pourcentage pour créer une fraction, puis à obtenir la forme la plus basse de la fraction pour déterminer le nombre, mais je ne pouvais pas trouver un moyen de le faire fonctionner avec l'arrondi des résultats.

Gareth
la source
Hmm, cela échoue pour le nouveau cas de test. Je vais devoir chercher une solution.
Gareth
4

Python, 154

def r(l):
 v=0
 while 1:
  v+=1;o=[round(y*v/100)for y in l];s=sum(o)
  if s: 
    if all(a==b for a,b in zip(l,[round(y*1000/s)/10for y in o])):return s
veste
la source
+1 Ça a l'air bien! ideone.com/k2Mgb . J'ai essayé de trouver un cas pathologique pour le casser et je n'ai pas pu.
mellamokb
Je ne peux pas générer sur ideone en raison d'un dépassement de délai, mais pour quel résultat obtenez-vous [0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,99.6]?
mellamokb
hmm ... une demi-heure et le programme est toujours en cours d'exécution. Je pense que c'est probablement sûr de dire que c'est un disjoncteur. cependant, je ne vois pas comment cela peut être une réponse valide car elle totalise 100,5% et non 100%
Blazer
2
1/2000 = 0.05%( 0.1%arrondi) et 1991/2000 = 99.55%( 99.6%arrondi). Le total est donc de 100%, mais l'arrondi le rend vraiment déroutant.
grc
3

VBA - 541

Cela a des erreurs flagrantes, mais c'était ma tentative de trouver une solution non triviale / en boucle jusqu'à ce que j'obtienne le bon numéro. Je ne l'ai pas entièrement joué au golf, bien que je ne pense pas qu'il y ait beaucoup à ajouter à cet égard. Cependant, j'ai passé trop de temps là-dessus et ça me fait mal à la tête maintenant. Sans oublier que les règles sont probablement très brisées et s'appliquent plus ou moins à ces exemples uniquement.

Cela fonctionne très bien pour de nombreux tests simples que j'ai exécutés (c'est-à-dire même des totaux, 2 ou 3 entrées) mais cela échoue pour certains des tests présentés par le défi. Cependant, j'ai trouvé que si vous augmentez la précision décimale de l'entrée (en dehors de la portée du défi), la précision s'améliore.

Une grande partie du travail consiste à trouver le gcd pour l'ensemble des nombres fournis, et j'ai en quelque sorte réussi à le faire Function g(), bien qu'il soit à coup sûr incomplet et probablement une source d'au moins certaines des erreurs dans mes sorties.

L'entrée est une chaîne de valeurs délimitée par des espaces.

Const q=10^10
Sub a(s)
e=Split(s)
m=1
f=UBound(e)
For i=0 To f
t=1/(e(i)/100)
m=m*t
n=IIf(n>t Or i=0,t,n)
x=IIf(x<t Or i=0,t,x)
Next
h=g(n,x)
i=(n*x)/(h)
If Int(i)=Round(Int(i*q)/q) Then
r=i
ElseIf (n+x)=(n*x) Then
r=(1/(n*x))/h/m
ElseIf x=Int(x) Then
r=x*(f+1)
Else
z=((n+x)+(n*x)+m)*h
y=m/(((m*h)/(f+1))+n)
r=IIf(y>z,z,y)
End If
Debug.Print Round(r)
End Sub
Function g(a,b)
x=Round(Int(a*q)/q,3)
y=Round(Int(b*q)/q,3)
If a Then
If b Then
If x>y Then
g=g(a-b,b)
ElseIf y>x Then
g=g(a,b-a)
Else
g=a
End If
End If
Else
g=b
End If
End Function

Cas de test (entrée ==> attendu / retourné):

Passed:  

"95 5" ==> 20/20
"90 10" ==> 10/10
"46.7 53.3" ==> 15/15
"4.7 30.9 40.4 23.8" ==> 42/42
"44.4 44.4 11.1" ==> 9/9
"26.7 53.3 20.0" ==> 15/15
"48.4 13.7 21.6 6.5 9.8" ==> 153/153
"0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 99.55" ==> 2000/2000
"0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 98.65" ==> 2000/2000
"0.149925 0.149925 0.149925 0.149925 0.149925 0.149925 0.149925 0.149925 0.149925 98.65067" ==> 667/667


Failed:  

"0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 99.6" ==> 2000/1000
"0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 98.7" ==> 2000/5000
"0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 98.7" ==> 667/1000
"0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 0.14 98.65" ==> 667/10000
"0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 97.8" ==> 401/500
"0.24 0.24 0.24 0.24 0.24 0.24 0.24 0.24 0.24 97.75" ==> 401/235
"0.249377 0.249377 0.249377 0.249377 0.249377 0.249377 0.249377 0.249377 0.249377 97.75561" ==> 401/14010
Gaffi
la source
vous pouvez perdre 6 octets en convertissant Debug.Print enDebug.?
Taylor Scott
2

C # (.NET Core) , 286 octets

double M(string[]a){var p=a.Select(double.Parse).ToList();var n=p.Select(x=>1d).ToList();var c=2;for(;;){Func<double,double>f=x=>Math.Round(x*1000/c,(MidpointRounding)1)/10;if(n.Select(f).Zip(p,(x,y)=>x==y).All(z=>z)&&c==n.Sum())return c;c++;n=n.Zip(p,(x,y)=>x+(f(x)<y?1:0)).ToList();}}

Essayez-le en ligne!

Beaucoup d'octets enregistrés grâce à Peter Taylor et Embodiment of Ignorance

Cristian Lupascu
la source
Comment pourrais-je le modifier pour le tester sur ideone.com?
Gareth
Je pense que vous en manque un }à la fin.
grc
@Gareth J'ai essayé de l'exécuter sur ideone.com, mais je pense qu'il utilise une version du framework .NET antérieure à 4.0, car il ne reconnaît pas la Zipméthode Linq .
Cristian Lupascu
@grc Merci de l'avoir signalé. Mis à jour.
Cristian Lupascu
1
@Gaffi: Non, C # a un typage strict (comme Java) donc ce doit être un booléen. Puisque 1>0est plus court que true, il est préféré.
mellamokb
0

Python 3 , 140 139 137 137 octets

f=lambda l,m=1,i=0,c=0,x=0:round(x*100,1)-l[i]and(x<1and f(l,m,i,c,x+1/m)or f(l,m+1))or l[i+1:]and f(l,m,i+1,c+x)or c+x-1and f(l,m+1)or m

Essayez-le en ligne!

Donne la bonne réponse pour les deux premiers cas de test et se heurte aux limites de récursivité de Python pour les autres. Ce n'est pas très surprenant, car chaque vérification est effectuée à un nouveau niveau de récursivité. C'est court, cependant ...

(Une explication des variables utilisées peut être trouvée dans le lien TIO)

f=lambda l,m=1,i=0,c=0,x=1:round(x*100,1)-l[i]and(x and f(l,m,i,c,x-1/m)or f(l,m+1))or l[i+1:]and f(l,m,i+1,c+x)or c+x-1and f(l,m+1)or m

devrait fonctionner pour 136 octets, mais ne fonctionne pas en raison de la précision du flottant.

ArBo
la source