Многие слышали про "парадокс" мартышки: если она сядет за печатную машинку и начнёт стучать по клавишам, то рано или поздно она всё-таки наберёт роман Толстого "Война и мир".

Делаем мартышку.

Вообще, у меня как у программиста совсем не складывается с нейронными сетями. И вот, недавно я пытался создать модель для написания текстов, даже частично осмысленных. Следовал инструкции с Хабра, но опять всё пошло не по плану.

В итоге мне это надоело, и я подумал так:

Каждое слово следует за другим, если оно не первое. Также каждое слово стоит перед другим, если не является последним. Если посмотреть, какое слово (с1) встречается после некоторого слова (с0) чаще других, а после сделать то же для нового слова (с1), то продолжая так дальше, мы наберём предложение, которое автор наберёт с максимальной вероятностью.

Это мы и будем делать.

Никакой сложной глупости, всё просто: операции с файлами, строками, списками и массивами, то, что я люблю.

Скучная часть

import pickle

Для начала необходимо прочитать все строки файлов книг.

for fi in range(17):
    f = open(str(fi) + ".txt")

    stat = f.readlines()

    data = []
    for i in stat:
        if i[0] != "—" and i != "":
            data.append(i[0:len(i)-1])

Далее разделим всё на предложения, убирая знаки (!""-;:.,?) и так далее.

stat = []
app = ""
for i in data:
    for j in i:
        if j != '!' and j != '.' and j != ',' and j != "-"  and j != "(" and j != ")" and j != ":" and j != "—" and j != "«" and j != "»" and j != ";":
            app += j
        else:
            stat.append(app)
            app = ""

Теперь разделим предложения на слова, но объединять в один список не будем, ведь слова из разных предложений с друг другом никак не связаны.

data = []
data1 = []
app = ""
for i in stat:
    for j in i:
        if j != " ":
            app += j
        else:
            data1.append(app)
            app = ""

    data1.append(app)
    app = ""
    data.append(data1)
    data1 = []

x = 0
for i in data:
    for j in i:
        if j == "":
            data[x].remove(j)
    x += 1

Теперь - главное: в список помещаем пары слов, следующих друг за другом.

stat = []
for i in data:
    count = 1
    for j in i:
        if count < len(i):
            app = []
            app.append(j)
            app.append(i[count])
            
            count += 1

        stat.append(app)

##удалим пустые ячейки
for i in stat:
    if i[0] == "" or i[1] == "":
        stat.remove(i)

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

data = []
for  i in stat:
    t = 0
    for j in data:
        if i[0] == j[0]:
            j.append(i[1])
            t += 1
            break

    if t == 0:
        data.append(i)

Теперь сохраним этот массив для генерации в другом файле программы.

try:
    with open("data0.pickle", "wb") as f:
        pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
except Exception as ex:
    print("Error during pickling object (Possibly unsupported):", ex)

Теперь перейдём в новую программу и создадим скрипт для генерации бреда.

import pickle
import random as r

Откроем сохранённый массив.

try:
    with open("data0.pickle", "rb") as f:
        data = pickle.load(f)
except Exception as ex:
    print("Error during unpickling object (Possibly unsupported):", ex)

А после спросим у пользователя:

  • Сколько предложений необходимо сгенерировать

  • Минимальная длина предложения

  • Максимальное превышение минимальной длины

Далее приступим к генерации. Программа берёт случайное первое слово С и ищет для него случайную пару С0. После она ищет пару С1 для С0 и так далее, пока может найти пару. Получится:

С, С0, С1,С2...

Если длина превышает минимальную и меньше максимальной, то программа выводит результат и приступает к следующей генерации, а иначе делает новую попытку.

stri = ""
print("длина:")
i = int(input())
print("лимит:")
limit = int(input())
print("разброс:")
viiiu = int(input())

d = r.randint(0, len(data)-1)
nexxt = data[d][r.randint(1, len(data[d])-1)]
stri += nexxt

count = 0
while count < i:
    t = 1
    while t != 0:
        t = 0
        for j in data:
            if j[0] == nexxt:
                nexxt = j[r.randint(1, len(j)-1)]
                stri += " " + nexxt
                t += 1
        if t == 0:
            stri += ". "
            if len(stri) > limit and len(stri) < limit + viiiu:                     
                print(str(count + 1) + ". " + stri)
                fill.write(str(count + 1) + ". " + stri + "\n")
                count += 1
            stri = ""

            d = r.randint(0, len(data)-1)
            nexxt = data[d][r.randint(2, len(data[d])-1)]
            stri += nexxt

В итоге мы получаем что-то типа:

Однако, нас это не должно радовать, ведь я сделал это с помощью 16 книг.

Попробуем построить предложения подлиннее, и напишем программу, чтобы узнать максимально возможную длину "предложения". К сожалению, программа начиталась книги про партизан:

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

У меня получилось максимум 681 символ. Возможно, когда-то программа войдёт в цикл типа:

... и я и ты и я да и мы и ты и вы и партизаны...

но я пока не обнаружил подобных явлений.

Теперь оставим только те слова, которые встречаются после предыдущих больше раз, чем другие. Делаем это в первой программе.

stat = []
count = 0
for i in data:
    stat.append([i[0],[],[]])

    for j in i[1:len(i)-1]:
        stat[count][2].append(i[1:len(i)-1].count(j))
        stat[count][1].append(j)
    count += 1

data = []
for i in stat:
    if len(i) > 2:
        if i[2] == []:
            stat.remove(i)
        else:
            print(i[2])
            data.append([i[0], i[1][i[2].index(max(i[2]))]])

Тогда data будет

[['pensando', 'a'], ['a', 'me'], ['IНикуда', 'я'], ['не', 'знаю'], ['часто', 'на'], ['на', 'меня'], ['как', 'будто'], ['моей', 'незнакомке'], ['Около', 'этого'], ['этого', 'села'], ['села', 'находятся'], ['может', 'быть'], ['для', 'того'], ['и', 'не'], ['я', 'не'], ['оттуда', 'возвращался'], ['глинскому', 'старосте'], ['у', 'меня'], ['которого', 'я'], ['до', 'того'], ['Глинного', 'не'], ['двух', 'верст'], ['только', 'на'], ['небольшой', 'холм'], ['лежит', 'усадьба'], ['домика', 'и'], ['всегда', 'случалось'], ['нее', 'глазами'], ['самый', 'жар'], ['всякий', 'раз'], ['раз', 'этот'], ['со', 'своими'], ['слепым', 'стариком'], ['Сидит', 'он'], ['давно', 'жуешь'], ['но', 'в'], ['он', 'всегда'], ['по', 'крайней'], ['согретых', 'щеках'], и т.д....

Да, теперь каждому слову соответствует лишь одно - самое частоупотребляемое.
Возьмём первое слово списка С0 и соответствующее ему С1, для С1 найдём в data С2, самое частое после С1 и продолжим далее, пока не найдём такое СХ, что не будет в data такого подмассива, как

[CX,  C(X+1)]

Я выразился сложно, но программа получилась простая:

import pickle
try:
    with open("data0.pickle", "rb") as f:
        data = pickle.load(f)
except Exception as ex:
    print("Error during unpickling object (Possibly unsupported):", ex)


count = 0
stat = data
for i in data:
    count = 0
    for j in stat:
        if j[0] == i[1]:
            count += 1

    if count < 1:
        data.remove(i)

resultr = []
res = ""
for i in data:
    res += i[0] + " "
    res += i[1] + " " 
    count = 1
    nexxt = i[1]

    while count != 0:
        count = 0        
        for j in data:
            if j[0] == nexxt:
                res += j[1] + " "
                nexxt = j[1]
                count += 1
                break

    resultr.append(res) 
    res = ""

print("result: " + str(max(resultr, key=len)))        

По описанному выше алгоритму мы в коде составляем предложения, записываем их в массив и выбираем самое длинное.

Алгоритм готов, скучнейшая часть пройдена, далее только посмотрим на то, какие предложения каким книгам соответствуют, и передают ли стиль автора.

Самое интересное

Я просто напишу это в формате "Книга - предложение":

Гоголь - Шинель:
всё-таки останется какой-нибудь смазливой

Тургенев - Три встречи:
упрекать вас там что я не знаю отчего она

Тургенев - Первая любовь:
половину первого раза три спустя я не мог

Чехов - Палата номер 6:
Ивану Дмитричу казалось каждый день утром до обеда

Тургенев - Отцы и дети:
отрицательного направления?– Что это все равно что он не мог

Гоголь - Нос:
кондитерской никого нет носа совершенно гладкое место шуткам

Тургенев - Муму:
встретить Герасима привезли в Москву

Тургенев - Записки охотника:
имеют способность улыбаться во как говорится у меня в сторону и не было нечего сказать что ж

Пушкин - Дубровский:
русскому обыкновению своему разгоряченный

Гоголь - Вий:
большую дорогу усеивали грамматики начинали прежде всех сторон

Лермонтов - Герой нашего времени:
Последовало молчание самым прозаическим образом ее руку и я ему

Видите, как сильно Пушкин отличается? Это неспроста...

Обычный текст

Стиль будет мой - много воды, мало смысла:

Однажды солнечным весенним утром десятиклассник Антон шёл в школу. Проходя по тенистой аллее тополей, посаженных здесь пятьдесят лет назад с рюкзаком за спиной, он смотрел на цветы, посаженные на ухоженных клумбах его города. Через арку деревьев он вышел к перекрёстку. Яркие солнечные лучи освещали его, но люди, спешащие по своим делам - мамы с детьми, важные мужчины в строгой одежде, подростки с колонками - не замечали ни солнца, ни машин, а старались перебраться на противоположную сторону дороги. Лишь только Загорался красный свет, автомобили срывались с места и тоже пытались как можно быстрее миновать пешеходный переход. Антон подошёл к переходу и, нажав кнопку системы автоматического регулирования движения, стал ждать. Машин в тот день было немного, но от этого они ехали только быстрей по многополосному проспекту. В его центре и по краям были поставлены то ли прочные заборчики, то ли слабые отбойники, отливающие холодным металлическим блеском. Загорелся зелёный свет светофора, и пешеходы поспешили через зебру. Пройдя треть перехода, Антон посмотрел налево, и удивился тому, что автомобиль ехал не прямо, а вилял по сторонам дороги, часто стучась об отбойники. Мальчик остановился. Тут же в его голове появился расчёт: до автомобиля где-то двести метров, скорость машины 90 км/ч, отрицательным ускорением можно пренебречь. Равно восемь секунд чистыми. Пока все посмотрят плюс полсекунды, пока поймут секунда, пока сдвинутся две секунды, остаётся четыре с небольшим...
- Машина! - крикнул он, указывая на приближающееся авто, и хотел было рвануть на другую сторону дороги, но наткнулся (и чуть не задавил) одного из детей молодой мамы. Тут же он схватил их всех (троих или двоих) и побежал через толпу. Мать побежала за ним. Не успел Антон пробежать и метра, как заметил кого-то, оцепеневшего то страха, и, хотя оно было почти с него ростом, подхватил и его (её?), хотя он усердно сопротивлялся. Выбежав на другую часть дороги, он оглянулся, но, споткнувшись, стал падать. Почти инстинктивно он в воздухе повернулся, падая под детей, и в этот момент услышал хруст, скрежет, сдавленный крик, а после упал, ударившись головой об мраморный бордюр и отключился.

И в результате я получил.....

Немного похоже на пересказ текста, но он что-то уж очень краток

И напоследок немного "юмора":

Да, в итоге я нашёл циклы, но они мешали выполнению главной задачи, и я их пресёк.

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

  • много странных предложений, над которыми долго смеялся

  • что-то наподобие пересказывателя

  • Предложения, изредка неплохо характеризующие стиль автора.

Спасибо за внимание!

Репозиторий с исходниками чтоб вам не мучиться.

Комментарии (2)


  1. saege5b
    14.07.2023 21:14

    Смех смехом, но классический косяк типа: - Я с утра был в прекрасном распоряжении дюха - встречается всё чаще и чаще в книгах.

    А на ошибочные окончания несогласованных времён и полов даже внимания не обращаешь.


  1. erogov
    14.07.2023 21:14
    +2

    Поздравляю, вы изобрели цепь Маркова.

    и я и ты и я да и мы и ты и вы и

    Напомнило