Давайте рассмотрим некоторые книги по их обложкам


47

Все знают, что содержание делает вопрос. Но хороший заголовок тоже помогает, и это первое, что мы видим. Пришло время превратить это первое впечатление в программу и выяснить, какие названия получают больше голосов.

Настоящим вам предлагается написать программу или функцию, которая принимает название вопроса PPCG в качестве входных данных и возвращает прогноз его оценки.

Например, вы можете получить Counting Grains of Riceв качестве входных данных, и 59в этом случае вы пытаетесь вернуть что-то близкое к результату . Нецелочисленные догадки хороши, но догадки на уровне или ниже -20- нет.

Вот данные для тестирования и оценки:

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

Оценка: Ваша программа будет выполняться по каждому вопросу в истории этого сайта (PPCG), не считая закрытых вопросов. Затем эта функция ln(score + 20)будет применена к каждому результату и каждому предположению. Среднеквадратическая ошибка между двумя результирующими наборами значений является вашей оценкой. Ниже - лучше.

Например, программа, которая угадывала 0 каждый раз, набрала бы 0,577, а программа, которая угадала 11 каждый раз, получит 0,362.

Пожалуйста, рассчитайте свой счет и включите его в заголовок вашего ответа. Пожалуйста, также включите прогноз вашей программы о том, сколько голосов получит этот вопрос.

Ограничения:

  • Для предотвращения чрезмерного жесткого кодирования, не более 1000 символов.

  • Должен работать на всех данных, указанных выше, менее чем за минуту на разумной машине.

  • Стандартные лазейки закрыты.


Вот тестер, написанный на Python, для вашего использования и / или устранения неясностей:

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
Хорошая идея, но обидно, что набор данных не стабилен, поэтому через некоторое время результаты станут недействительными. Существует также небольшая вероятность стратегического голосования: любой, кто ответит на этот вопрос и получит значок vox-populi на той же неделе, должен быть с подозрением воспринят! ;-)
Уровень Река St

1
Будет ли название включать или исключать такие вещи, как [closed]и [on hold], где это применимо?
es1024 11.11.14

4
@steveverrill Ну, с другой стороны, с течением времени, мы сможем увидеть, хорошо ли программы работают на будущих постах, а также на прошлых.
Исаак

6
Трудно победить жесткое кодирование. Каждый жестко заданный вопрос с наибольшим количеством голосов может снизить оценку до 0,4. И, похоже, не так много общего, хаха. Я предсказываю, что ответы будут соревноваться в том, как разместить столько жестко закодированных результатов в 1000 байтов.
Половина

5
Вы не должны использовать полный набор вопросов в качестве набора тестов. Вы должны предварительно выбрать случайное число (10% -20%) и определить его как свой набор тестов (но никому не говорить, что это такое). Намного проще создать алгоритм, который предсказывает прошлую историю, чем тот, который имеет будущую прогностическую ценность (т. Е. Тот, который хорошо работает на любом данном подмножестве). (Было бы еще лучше убрать эти 10% из того, что мы можем видеть вообще, но это не очень хорошо сработало бы.)
Джо

Ответы:


9

Python 2, 991 символ, оценка 0,221854834221, прогноз 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)

Объяснение:

Это бесстыдное жесткое кодирование, но попытка сделать это эффективно.

Предварительная обработка:

В отдельном коде я хэшировал каждый заголовок со значением от 0 до 256 ^ 2-1. Давайте назовем эти значения мусорными ведрами. Для каждого бина я подсчитал средний балл. (Среднее необходимо, потому что для крошечной доли бинов есть коллизии - более 1 хэша заголовков в одном бине. Но для подавляющего большинства каждый заголовок отображается в свой бин).

Идея, лежащая в основе 2-байтового кода для каждого заголовка, заключается в том, что одного байта недостаточно - мы получаем слишком много коллизий, поэтому мы не знаем, какой счет назначить каждому 1-байтовому бину. Но с 2-байтовыми бинами почти нет столкновений, и мы фактически получаем 2-байтовое представление каждого заголовка.

Тогда ранг закрома - вычислить выигрыш в счете , если мы относим этот BIN его вычисленное значение, а не просто угадать 11. Возьмите верхний N бункера, и код их в строку (которая d в реальном коде).

Кодировка: ключ бина кодируется как 2 байта. значение кодируется с использованием 1 байта. Я нашел значения между -8 и 300 + что-то, поэтому пришлось немного сжать, чтобы получить его в 1 байт: x -> (x + 8) / 2.

Актуальный код:

считайте d как байтовые триплеты, расшифровав код, описанный выше. Когда дан заголовок, вычислите его хеш (по модулю 256 ^ 2), и, если этот ключ найден в dict, верните значение, на которое он отображается. В противном случае верните 11.


3
Предложение: Средний балл не так хорош. Посмотрите на функцию оценки заданий, она нелинейная.
Дедупликатор

1
@Deduplicator Спасибо, я понял, что после того, как я закончил. Дело в том, что для 99% бинов нет коллизий, поэтому среднее значение на самом деле представляет собой просто счет одного заголовка, который отображается на этот бин.
Офри Равив

16

Javascript ES6

Оценка: 0,245663
Длина: 1000 байт
Прогнозы: 5

(Я предполагаю, что вопрос из-за неожиданной лавины понижений.: P)

Минимизированный

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]));

расширенный

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] )
);

Функция Sпринимает строку (заголовок) и возвращает ее оценку.

Примечания о поведении:

  • ≤ 70 названий голосов обрабатываются отдельно от> 70 ​​названий голосов
  • ≤ 70 заголовков голосов сортируются по бинам с использованием сложного алгоритма оптимизации потенциальных возможностей отслеживания ключевых слов, который никоим образом не напоминает строковую хеш-функцию
  • после небольшого количества счастливого исчисления оказывается, что оптимальное предположение для каждого бина - это просто геометрическое среднее (голосов + 20) для всех заголовков в бине, минус 20
  • оптимальные догадки для всех 479 бинов затем кодируются как 479-символьная строка base-70
  • для> 70 заголовков голосов заголовкам присваиваются уникальные трехзначные коды base-36, сгенерированные с использованием современной техники хеширования, которая гарантирует отсутствие коллизий с другими> 70 заголовками голосов и ложное обнаружение ≤ 70 заголовков голосов. Этот современный метод никоим образом не напоминает попытки случайного подсчета бинов, пока не произойдет столкновение.
  • > 70 кодов заголовков голосов и их подсчет голосов кодируются в виде строки (6 байт на заголовок), которая преобразуется в простую справочную таблицу. Таким образом, процедура имеет нулевую ошибку для всех> 70 голосов.

10

Python 2, оценка = 0,335027, 999 символов, прогноз 11,34828 по этому вопросу

Просто чтобы мяч катился. Это нигде не оптимально.

Причудливая вещь SVM - это всего лишь моя случайная идея, и мне хотелось реализовать ее, так что вот она. Это улучшает базовую линию на 0,02 балла, поэтому я достаточно доволен этим. Но чтобы показать, что жесткое кодирование ввода является источником большинства улучшений, я также жестко кодирую некоторые ответы.

Без жесткого кодирования оценка составляет 0,360 (и на самом деле все прогнозы около 11, ха-ха)

Я использую scikit-learn и 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

Я не уверен, что понимаю - вы читаете результаты из внешнего файла? Так почему бы просто не предсказать sd [t]? Это даст оценку 0 ...
Офри Равив

2
Потому что это не было бы весело = p
justhalf

4

Python 2, 986 символов, оценка 0.3480188, прогноз 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]

Соответствующая функция есть J.

Программа, по сути, наивная байесовская, использующая в качестве функций заглавные слова, но она чрезвычайно ограничена благодаря ограничению числа символов. Насколько ограничен? Что ж...

  • Для каждого заголовка мы конвертируем в нижний регистр и смотрим только слова длиной не менее 4 букв. Затем мы берем первые три буквы каждого из этих слов в качестве признаков. Мы пропускаем пунктуацию, чтобы сэкономить на символах.
  • Мы выбираем только буквенные триплеты, длина которых не менее 19 слов (они хранятся wвыше). Сжатие выполняется путем перестановки триплетов так, чтобы присутствовало как можно больше удвоенных букв, и эти пары заменяются соответствующими прописными буквами ASCII (например, fiLoNoN ... → fil, lon, non, ...)
  • Для каждого триплета мы смотрим на оценки названий, в которых он появляется, и вычисляем среднее и стандартное отклонение оценок. Тогда преобразование тех целых чисел и хранить их в m, sвыше, используя тот факт , что средние / сд являются не более чем на 90 ( что позволяет прямое кодирование ASCII, так как есть 95 версия для печати ASCII)
  • G это нормальная функция распределения - мы округляем e до 2dp и обратный квадратный корень от 2 pi до 1 dp, чтобы сэкономить на символах.

В целом, экстремальный предел числа символов сделал это одной из худших идей, которые я когда-либо придумывал, но я очень доволен тем, как много мне удалось втиснуть (хотя это не очень хорошо работает). Если у кого-то есть идеи для сжатия, пожалуйста, дайте мне знать :)

(Спасибо KennyTM за указание на мое бессмысленное сжатие)


Если я неправильно выполнил код, ваш код сжатия даже длиннее, чем распакованный результат ... w='grge…scse';w=[w[i:i+2]for i in range(0,len(w),2)]это 165 байт, а ваш C=lambda:…;w=C('…')- 179 байт.
Kennytm

@KennyTM О, спасибо - я так много возился с кодом, пытаясь соответствовать пределу числа символов, что потерял контроль над всем сжатием. : P
Sp3000 11.11.14

4

Python 2, 535 символов, оценка 0,330910, прогноз 11,35

Средняя оценка для названий, содержащих каждое слово, затем используйте верхние и нижние 50 слов, чтобы, возможно, изменить оценку BASE в guess(title)функции.

Код 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

С

Оценка: неизвестно
Длина: 5 байт
Прогнозы: 5

Golfed:

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

Ungolfed:

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

Запрос оценок дает средний балл 5.

У меня нет возможности проверить это в настоящее время, другие могут запускать / редактировать.


Далее Gulfed: int s () {return 5;}
Иисус Навин

«Настоящим вам предлагается написать программу или функцию, которая принимает название вопроса PPCG в качестве входных данных и возвращает прогноз его оценки». - Извините, но нет: 0
Joshpbarron

Я видел платформу, на которой, если вы забыли main (), ваша первая функция была main (). Может быть, он зависит от этого.
Джошуа
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.