Vamos julgar alguns livros pelas capas


47

Todo mundo sabe que o conteúdo faz a pergunta. Mas um bom título também ajuda, e é a primeira coisa que vemos. É hora de transformar a primeira impressão em um programa e descobrir que tipos de títulos recebem mais votos.

Você é desafiado a escrever um programa ou função que tome o título de uma pergunta do PPCG como entrada e retorne uma previsão de sua pontuação.

Por exemplo, você pode receber Counting Grains of Ricecomo entrada e tentaria retornar algo próximo à pontuação, 59nesse caso. As suposições não inteiras são boas, mas as suposições iguais ou inferiores -20não são.

Aqui estão os dados, para teste e pontuação:

http://data.stackexchange.com/codegolf/query/244871/names-and-upvotes

Pontuação: seu programa será executado em todas as perguntas no histórico deste site (PPCG), sem contar as perguntas fechadas. A função ln(score + 20)será aplicada a cada pontuação e a cada palpite. O erro quadrático médio entre os dois conjuntos de valores resultantes é a sua pontuação. Menor é melhor.

Por exemplo, um programa que adivinhou 0 todas as vezes teria 0,577, enquanto um programa que adivinharia 11 todas teria 0,326.

Calcule sua pontuação e inclua-a no título da sua resposta. Inclua também a previsão do seu programa para quantas votações positivas esta pergunta receberá.

Restrições:

  • Para evitar códigos excessivos, no máximo 1000 caracteres.

  • Deve ser executado em todo o conjunto de dados acima em menos de um minuto em uma máquina razoável.

  • As brechas padrão estão fechadas.


Aqui está um testador escrito em Python, para seu uso e / ou para esclarecer ambiguidades:

import sys
import math
import csv

scores_dict = {}

with open(sys.argv[1], 'r') as csv_file:
    score_reader = csv.reader(csv_file)
    for score, title in score_reader:
        if score == 'Score':
            continue
        scores_dict[title] = int(score)

def rate_guesses(guesser):
    def transform(score):
        return math.log(score + 20) if score > -20 else 0
    off_by_total = 0
    lines_count = 0
    for title in scores_dict:
        guessed_score = guesser(title)
        real_score = scores_dict[title]
        off_by_total += (transform(real_score) - transform(guessed_score)) ** 2
    return (off_by_total/len(scores_dict)) ** .5

def constant11(title):
    return 11

print(rate_guesses(constant11))

19
Boa ideia, mas é uma pena que o conjunto de dados não seja estável, portanto as pontuações ficarão inválidas depois de um tempo. Há também uma pequena possibilidade de votação estratégica: quem responder a essa pergunta e ganhar um distintivo vox-populi na mesma semana deve ser visto com desconfiança! ;-)
Level River St

1
O título incluirá ou excluirá coisas como [closed]e [on hold], onde aplicável?
es1024

4
@steveverrill Bem, o outro lado disso é que, à medida que o tempo avança, poderemos ver se os programas se saem bem em publicações futuras e em publicações anteriores.
Isaacg 11/11

6
É difícil derrotar a codificação codificada. Cada pergunta mais votada e codificada pode reduzir em até 0,4 pontos. E parece não haver muito padrão comum também, haha. Estou prevendo que as respostas apenas competirão em como ajustar o resultado codificado em 1000 bytes.
justhalf

5
Você não deve usar o corpo completo de perguntas como seu conjunto de testes. Você deve pré-selecionar um determinado número (10% a 20%) aleatoriamente e defini-los como seu conjunto de testes (mas não contar a ninguém o que é isso). É muito mais fácil criar um algoritmo que prediz histórico passado do que um que tenha valor preditivo futuro (ou seja, que funcione bem em qualquer subconjunto). (Seria ainda melhor remover esses 10% do que podemos ver, mas isso realmente não funcionaria bem.) #
1111 Joe Joe

Respostas:


9

Python 2, 991 caracteres, pontuação 0,221854834221, prever 11

import base64
e={}
d=base64.decodestring('vgAcRAEVDAIsqgQYalYaggcjQKwVXAoZWAsYQg0Ckg4VlWEX9hEDRhMe0hckCBkeuhsW3CAWQiEm\nSiMZMiwgTDAZZjIcSLMZfDQDnjwCe2AVaEQCYWEBIEgnDEoXzk0e3lQb5FYVKlkVZlwB+F0XwmI/\nGmRcuGUXWmYdVGkbzmwafG8eaHMdInkggHonRn5sKoMXgIkpbowVOI4cNJAubpQdYpcydJgVypkA\nZpweMp8ZsqEcRKMghKQYkKVPPXEWMqkWHKwbjrEdzLIBNLMf1LQivrYC99UV9rxNRsABNMEiPzob\npc0ActAhn3gcrNUZYNcWYNov/t8VgOEXAuMYqOUWsqUiCPIefPWNbvtKevwWvP0Cl9UsjQMdWwQb\nfQdpJQgWYwkCZRLBjxMWWdkqHRkWNxwB6x8p2SEZyyICSyYcgysaOS0CUy8hezAaGeEVpTRQ3zUz\nZzkZRzohizwwST4c8UAdF0OqG9AXIuEYYRN6208nU1AktVEVJ1IVWeMkIVQXdY4D2VYYD/cYb1om\nG1xA0zoY3uUaRWAhWpBSHWUXQTxGe+cad20CO3AZX3EBw3IiMXcef3gecXsVGXwhw30VbX4W24BD\n6qyQ45YaYYgZ4YobbYwauY4bMY82HZEdO5YmQ5cV35sVxaMbY6gYNas576ws+bADO7QpN7hdLJ8B\n4Eoks8EYX8VU68cYWfcar82QOdAaxdEfQ9UiW/kXL9k2ddwCW90m694enqUCkeEBE+IYWvsfA1FC\nJ+spMVIjhe4WEP0fAfYax/c3MfgbgfkqP/0DLf4V\n')
for i in range(0,600,3):
 e[ord(d[i])*256+ord(d[i+1])]=ord(d[i+2])*2-8
def p(t):
 return e.get(hash(t)%256**2,11)

Explicação:

Esta é uma codificação vergonhosa, mas tentando fazê-lo com eficiência.

Pré-processando:

Em um código separado, eu dividi cada título em um valor entre 0 e 256 ^ 2-1. Vamos chamar esses valores de caixas. Para cada posição, calculei a pontuação média. (A média é necessária porque, para uma fração minúscula dos compartimentos, há colisões - mais de 1 hashes de título no mesmo compartimento. Mas, para a grande maioria, cada título é mapeado para um compartimento próprio).

A idéia por trás do código de 2 bytes para cada título é que 1 byte não é suficiente - temos muitas colisões, então não sabemos realmente qual pontuação atribuir a cada compartimento de 1 byte. Mas com compartimentos de 2 bytes, quase não há colisões, e obtemos efetivamente uma representação de 2 bytes de cada título.

Em seguida, classifique os compartimentos - calcule o ganho na pontuação se atribuirmos a este compartimento seu valor calculado, em vez de apenas adivinhar 11. Pegue os N compartimentos superiores e codifique-os em uma string (que é d no código real).

A codificação: a chave do compartimento é codificada como 2 bytes. o valor é codificado usando 1 byte. Encontrei valores entre -8 e 300 + algo, então tive que apertar um pouco para obter 1 byte: x -> (x + 8) / 2.

Código atual:

leia d como trigêmeos de bytes, decodificando a codificação explicada acima. Quando um título é fornecido, calcule seu hash (módulo 256 ^ 2) e, se essa chave for encontrada no dict, retorne o valor para o qual ele mapeia. Caso contrário, retorne 11.


3
Uma sugestão: a pontuação média não é tão boa. Veja a função de pontuação de desafios, não é linear.
Deduplicator

1
@Duplicator Obrigado, percebi que depois que eu terminei. O problema é que, para 99% dos compartimentos, não há colisões; portanto, a média é na verdade apenas a pontuação do título único que é mapeado para esse compartimento.
Ofri Raviv

16

Javascript ES6

Pontuação: 0.245663
Comprimento: 1000 bytes
Prevê: 5

(Acho que a pergunta se deve a uma avalanche inesperada de votos negativos.: P)

Minificado

E="ABCDEFGHIJKLMNOPQRSTUVWXYZ";E=E+E.toLowerCase()+"0123456789!@#$%;*()";P="RCRHIFMGPGIDQKHMJLLRMLFJGKHEqHPOgJNKGPCHPJNUPOSGJQKKKMELMIJHLKIKNcKDOfSJLFHDILGKIOUKLMLLKMKIHGHJGIIJDGJKIHIIFIGMTIHFJMIKDJGQJKGMKJHPRJPLMGIOPIIIIPBYFMGLEIKIMMRUKFLFGJFKFTHPFODEQTGTHKIJDHNJGDGiHKNYLHHDLJHGILOEViKNEKGQZfHJMIISIJFRHKLJMLPIFJILKKKJKKJESHNLLIKIGKGJJJHKJRGULLSLRONJNEeLKIQGGPQIHPLEIHHIDXjQHNBKOGWWIENRbYoHINHNMKTNKHTGMIPXFJLLMLIHPPLDJJKFUMIQMOKJLJJKIJKNLHRILJIAIbJEZOGIELGHGLOINDPJMJeJWRIIJHSOZDOsOFRRIOIOTIJSGGJKFUIDOINGOcLQEJFEITLMNIIGIGIMG7LPSNLKVOKIFGHJITGOFUJIIRN";K={};"99r1501vz076mip077myv0733it280exx081gt9118i1g279dyx102uho203ejd07433z087uje097kdg1567ft2088rk275dmu1203ez106lih1763ei126f6q101aax084owh088aid161i9y179gvn236ptn3338vf132i55080fke101l4z3789ai281ulm081blm124euz074o5m07513z14117l095qdn092gl30757n5153".replace(/(...)(...)/g,(_,a,b)=>K[a]=1*b);D=40655;N=479;H=(s,T)=>(h=7,[...s].map(c=>h=~~(h*T+c.charCodeAt(0))),h);S=s=>(y=H(s,31),K[((y%D+D)%D).toString(36)]||E.indexOf(P[(H(s,48)%N+N)%N]));

Expandido

E = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
E = E + E.toLowerCase() + "0123456789!@#$%;*()";
K = {};
P = "RCRHIFMGPGIDQKHMJLLRMLFJGKHEqHPOgJNKGPCHPJNUPOSGJQKKKMELMIJHLKIKNcKDOfSJL" +
    "FHDILGKIOUKLMLLKMKIHGHJGIIJDGJKIHIIFIGMTIHFJMIKDJGQJKGMKJHPRJPLMGIOPIIIIP" +
    "BYFMGLEIKIMMRUKFLFGJFKFTHPFODEQTGTHKIJDHNJGDGiHKNYLHHDLJHGILOEViKNEKGQZfH" +
    "JMIISIJFRHKLJMLPIFJILKKKJKKJESHNLLIKIGKGJJJHKJRGULLSLRONJNEeLKIQGGPQIHPLE" +
    "IHHIDXjQHNBKOGWWIENRbYoHINHNMKTNKHTGMIPXFJLLMLIHPPLDJJKFUMIQMOKJLJJKIJKNL" +
    "HRILJIAIbJEZOGIELGHGLOINDPJMJeJWRIIJHSOZDOsOFRRIOIOTIJSGGJKFUIDOINGOcLQEJ" +
    "FEITLMNIIGIGIMG7LPSNLKVOKIFGHJITGOFUJIIRN";

   ("99r1501vz076mip077myv0733it280exx081gt9118i1g279dyx102uho203ejd07433z087u" +
    "je097kdg1567ft2088rk275dmu1203ez106lih1763ei126f6q101aax084owh088aid161i9" +
    "y179gvn236ptn3338vf132i55080fke101l4z3789ai281ulm081blm124euz074o5m07513z" +
    "14117l095qdn092gl30757n5153").
        replace( /(...)(...)/g, (_,a,b) => K[a] = 1*b );

D = 40655;
N = 479;
H = (s,T) => (
    h = 7,
    [...s].map( c => h = ~~(h*T + c.charCodeAt(0)) ),
    h
);

S = s => (
    y = H( s, 31 ),
    K[((y%D + D)%D).toString( 36 )] || E.indexOf( P[(H( s, 48 )%N + N)%N] )
);

A função Saceita uma sequência (título) e retorna sua pontuação.

Notas sobre comportamento:

  • ≤ 70 títulos de voto são tratados separadamente de> 70 títulos de voto
  • ≤ 70 títulos de votos são classificados em caixas usando um algoritmo de otimização de potencial de rastreamento de palavras-chave holonômico altamente sofisticado que não se parece com uma função de hash de string
  • depois de um pouco de cálculo feliz, verifica-se que o palpite ideal para cada compartimento é simplesmente a média geométrica dos (votos + 20) para todos os títulos no compartimento, menos 20
  • as suposições ideais para todos os 479 compartimentos são codificadas como uma sequência de base 70 de 479 caracteres
  • para> 70 títulos de voto, os títulos recebem códigos únicos de base 36 de 3 dígitos gerados usando uma técnica de hash de ponta que garante nenhuma colisão com outros títulos de> 70 votos e nenhuma detecção falsa de ≤ 70 títulos de voto. Essa técnica de ponta não se parece em nada com a tentativa de contagem aleatória de posições até que ninguém produza colisões.
  • os códigos de título de voto> 70 e sua contagem de votos são codificados em uma sequência (6 bytes por título), que é convertida em uma tabela de pesquisa simples. A rotina, portanto, tem zero de erro para todos os títulos> 70 votos.

10

Python 2, Pontuação = 0,335027, 999 caracteres, Preveja 11,34828 para esta pergunta

Só para fazer a bola rolar. Isso não é o ideal.

A coisa chique do SVM é apenas minha ideia aleatória e tive vontade de implementá-la, então aqui está. Isso melhora a linha de base em 0,02 pontos, então estou feliz o suficiente com isso. Mas para mostrar que a entrada codificada é a origem da maior parte da melhoria, eu também codifico algumas respostas.

Sem a codificação codificada, a pontuação é de 0,360 (e, na verdade, todas as previsões são de 11, haha)

Estou usando o scikit-learn e o nltk

import sys
import math
import csv
from sklearn.feature_extraction.text import TfidfVectorizer as TV
from sklearn.svm import SVR
from nltk.stem.porter import PorterStemmer as PS
sd = {}
lr = None
tv = None
with open(sys.argv[1], 'r') as cf:
    sr = csv.reader(cf)
    for sc, t in sr:
        if sc == 'Score':
            continue
        sd[t] = int(sc)
ts,scs = zip(*sd.items())
s = PS()
def analyzer(st):
    r = []
    for word in st.split():
        if word.isdigit():
            r.append('<NUM>')
        elif not word.isalnum():
            r.append('<PUNCT>')
        else:
            r.append(s.stem(word.lower()))
    return r
tv = TV(min_df=25, stop_words='english', analyzer=analyzer)
ti = tv.fit_transform(ts)
lr = SVR()
lr.fit(ti, scs)
d={'4 w':378,'y 42':333,'eeta':280,'an Got':279,'s 2 ':275,"e I'":208,'r CP':203,'? No':179,'B.O':156}
def c(t):
    for s in d.keys():
        if s in t:
            return d[s]
    t = tv.transform([t])
    r = lr.predict(t)[0]+1.5
    return r

Não sei se entendi - você leu as pontuações de um arquivo externo? Então, por que não apenas prever sd [t]? Isso daria uma pontuação de 0 ...
Ofri Raviv

2
Porque isso não seria divertido = p
justhalf

4

Python 2, 986 caracteres, pontuação 0,3480188, prever 12

M,S=14.29,23.02
D=lambda x:[ord(y)-32 for y in x]
w='fiLoNoNuMiNoTiMoShOnLoGoLeThRantexgensuBaSqUnInPeReGaMuLinprOuThInThEvEnClOpExPyThADdiSoLiSiMaTrEqUsImAsCoManstARrePoWoReawhapoLandradeTyPaLOsoReCreprediVeReSpebeTiPrImPladaTrihelmakwhicolpaReValpaTrafoROsoumovfinfunpuzyoufaSeQuiwhecoDeChagivcouchehanpoStrdiGraconjavwricalfrowitbinbrafrabuipoi'
for i in range(26):w=w.replace(chr(65+i),chr(97+i)*2)
w=[w[i:i+3]for i in range(0,372,3)]
m=D("(+),+)+=*...+..++'(*)5)/2)*43++16+0,+33*4*/(0.'+>-)+13+(2?8+6;,3;43+4(+.('(,.*.*+56+6)0((++(B,))+E0,-7/)/*++),+***)2+(3(.*)'")
s=D("))'B)'*j+:51(*3+0')+)^'/<-+MG,-1=),-0:A+T&J&K1%,O*()4Y-%:_A.=A*C]MJ-N%)5(%%-0+).*3Q(M&0'%(+$p*)()a8:-T?%5(-*'/.'+)+@,'J&1'&&")
def G(x,m,s):s=s or 1e-9;return(.4/s)*(2.78**(-(x-m)**2./(2*s*s)))
d={w[i]:(m[i],s[i])for i in range(124)}
def Z(t,c):
 p=1
 for W in t.split():
  if len(W)>3:
   W=W[:3].lower()
   if W in d:p*=G(c,*d[W])
 return p*G(c,M,S)
def J(t):return max([(Z(t,r),r)for r in range(-9,99)])[1]

A função relevante é J.

O programa é essencialmente Naive Bayes usando as palavras-título como recursos, mas é extremamente restrito graças ao limite de caracteres. Quão restrito? Bem...

  • Para cada título, convertemos para minúsculas e olhamos apenas para as palavras com pelo menos 4 letras. Em seguida, tomamos as três primeiras letras de cada uma dessas palavras como características. Ignoramos a pontuação de exclusão para economizar caracteres.
  • Nós escolhemos apenas trigêmeos de letras que estão no início de pelo menos 19 palavras (estas são armazenadas wacima). A compactação é feita reorganizando os trigêmeos para que o maior número possível de letras dobradas esteja presente, e esses pares são substituídos por suas maiúsculas ASCII correspondentes (por exemplo, fiLoNoN ... → fil, lon, non, ...)
  • Para cada trigêmeo, examinamos as pontuações dos títulos em que aparecem e calculamos a média e o desvio padrão das pontuações. Em seguida, converter os de números inteiros e armazená-los na m, sacima, usando o facto de que o / SD média são, no máximo, 90 (permitindo uma codificação ASCII direta, uma vez que existem 95 ASCII imprimível)
  • G é a função de distribuição normal - arredondamos e para 2dp e a raiz quadrada inversa de 2 pi para 1 dp para economizar em caracteres.

No geral, o limite extremo de caracteres tornou essa uma das piores idéias que já tive, mas estou muito feliz com o quanto consegui me concentrar (mesmo que não funcione muito bem). Se alguém tiver idéias melhores para compactação, entre em contato :)

(Obrigado ao KennyTM por apontar minha compactação inútil)


A menos que eu tenha executado o código incorretamente, seu código de compactação é ainda maior que o resultado descompactado ... w='grge…scse';w=[w[i:i+2]for i in range(0,len(w),2)]é de 165 bytes enquanto o seu C=lambda:…;w=C('…')é de 179 bytes.
kennytm

@KennyTM Oh, obrigado - eu tenho mexido tanto com o código, tentando ajustar o limite de caracteres que perdi a noção de toda a compressão. : P
Sp3000 11/11

4

Python 2, 535 caracteres, pontuação 0,330910, prevê 11,35

Faça a média da pontuação dos títulos que contêm cada palavra e, em seguida, use as 50 palavras superior e inferior para possivelmente modificar a pontuação BASE na guess(title)função.

Código Python:

BASE = 11.35
word={}
for sc,ti in csv.reader(open(sys.argv[1])):
    if sc == 'Score': continue
    parts = re.findall(r"[-a-zA-Z']+", ti.lower())
    for p in parts:
        a, c = word.get(p, (0.0,0))
        word[p] = (a+int(sc), c+1)

rank = sorted([(sc/ct,wd) for wd,(sc,ct) in word.items() if ct > 2])
adjust = rank[:50] + rank[-50:]

def guess(title):
    score = BASE
    parts = re.findall(r"[-a-zA-Z']+", title.lower())
    for sc,wd in adjust:
        if wd in parts:
            score *= 0.7 + 0.3*sc/BASE
    return score

3

C

Pontuação: Desconhecido
Comprimento: 5 bytes
Prevê: 5

Golfe:

int s(char *p){return 5;}

Ungolfed:

int s(char *p)
{
   return 5;
}

Uma consulta às pontuações fornece uma pontuação média de 5.

Eu não tenho a capacidade de testá-lo no momento, outros podem executar / editar.


Mais Gulfed: int s () {return 5;}
Joshua

"Você é desafiado a escrever um programa ou função que tome o título de uma pergunta do PPCG como entrada e retorne uma previsão de sua pontuação". - Desculpe, mas não: 0
Joshpbarron 12/11

Eu vi uma plataforma na qual se você esqueceu main (), sua primeira função foi main (). Talvez ele esteja dependendo disso.
Joshua
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.