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

В этой статье я опишу, как с помощью ChatGPT и JavaScript выяснил, что реально могу победить в конкурсе, и как в итоге пришёл к победе.

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

Тем не менее…

Даже в таких условиях я вполне могу перемещаться по дому с банкой WD-40, устраняя различные мелкие неисправности.

Собственно, поэтому моё внимание и привлёк видео-конкурс WD-40 Repair Challenge.

В целом я не особо люблю всяческие онлайн-конкурсы. Я предпочитаю, когда правила выстроены так, чтобы в них было реально выиграть.

Поэтому для начала давайте разберём некоторые заявленные критерии и условия, которые сделали этот конкурс очень привлекательным для участия.

Три благоприятствующих момента


▍ 1. Нюанс в судейских критериях


Я участвую только в тех конкурсах, где победителей выбирают судьи, никогда не рассматривая случайный выбор или победу большинством голосов (конкурсы популярности).

В данном же случае во взвешенном списке критериев оценки присутствовала строка, которая в конечном счёте исключила 80% участников.



Присваивание 1/4 очков заявке только за то, что это видео, уже говорит о высокой приоритетности именно видеоматериалов.

Тем не менее на конкурс, помимо видео-заявок, принимались и заявки с фото. И поскольку такие заявки согласно условиям автоматически недополучали ¼ очков, я решил исключить всех соответствующих участников как неконкурентных, так как их максимальный балл мог составить лишь 75/100.

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



⚖️
Чем сложнее список критериев оценки, тем больше мне хочется принять участие в конкурсе. Никто не анализирует эти списки так, как я. Уж чего у меня полно, так это времени, детка…

▍ 2. Широкий список призов




Очевидно, что мы нацелены на первое место, но всего конкурс предполагал 16 денежных призов и 13 призов в виде неких материальных ценностей. Итого в общей сложности 29 призов. Неслыханно много.

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

▍ 3. Возможность выиграть более одного приза


Если вы можете создать несколько заявок (n), то этот нюанс вас определённо порадует.




Перевод выделенного текста. Количество заявок от одного участника не ограничено. Участники, реализовавшие несколько проектов и подавшие несколько заявок, могут получить несколько выигрышей. Примерная рыночная стоимость всех призов составляет $16 190.

Анализ конкурса


В общей сложности на конкурс было зарегистрировано 538 заявок. Вероятность победы равная n/(538+n) не особо обнадёживает, не так ли?

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

Сайт конкурса был создан с помощью Laravel Livewire. Livewire не возвращает из своего API сырые данные JSON о заявках на конкурс, передавая вместо них HTML. Это означало, что я не могу просто обратиться к конечной точке /submissions, чтобы получить информацию о каждой заявке — нужно было её скрейпить.

Я составил скрипт Playwright для постраничного перебора галереи заявок и сбора данных, необходимых для анализа конкурентов.

const { chromium } = require("playwright");

const startScraping = async () => {
    const browser = await chromium.launch({ headless: false });
    const context = await browser.newContext();
    const page = await context.newPage();
    await page.goto("https://repair.wd40.com/gallery");
    // Клик по кнопке с надписью "Reject All"
    await page.click('button:text("Reject All")');
    await getAllEntries(page);
    await browser.close();
}

startScraping();

Поначалу всё довольно просто. Мы переходим в галерею заявок, отклоняем баннер с запросом об использовании куки и инициируем процесс сбора заявок.



▍ Сортировка заявок по категориям


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

Определить категорию каждой заявки можно было только по тому, какая SVG-иконка использовалась для её представления.

При дальнейшем рассмотрении выяснилось, что всего в конкурсе участвовало три вида заявок: video, photo, и step.



Категория Step подразумевала дополнительное сопровождение заявки письменными пошаговыми инструкциями. Поскольку отчётливое определение шагов, предпринятых для починки чего-либо в рамках вашего проекта, добавляло 10/100 очков, я понял, что наиболее перспективной в плане выигрыша является именно категория Step. Тогда я решил, что все мои заявки будут относиться именно к ней.

Ниже показано, как я реализовал функцию постраничного скрейпинга getAllEntries и присваивание каждой заявке категории:

// Определение категорий заявок по их иконкам
const IMG_ICON = `<path d="M14 9l-2.519 4-2.481-1.96-5 6.96h16l-6-9zm8-5v16h-20v-16h20zm2-2h-24v20h24v-20zm-20 6c0-1.104.896-2 2-2s2 .896 2 2c0 1.105-.896 2-2 2s-2-.895-2-2z"></path>`.trim();
const VIDEO_ICON = `<path d="M16 18c0 1.104-.896 2-2 2h-12c-1.105 0-2-.896-2-2v-12c0-1.104.895-2 2-2h12c1.104 0 2 .896 2 2v12zm8-14l-6 6.223v3.554l6 6.223v-16z"></path>`.trim();
const STEP_ICON = `<path d="m22 17.75c0-.414-.336-.75-.75-.75h-13.5c-.414 0-.75.336-.75.75s.336.75.75.75h13.5c.414 0 .75-.336.75-.75zm-18.25-2.75c.966 0 1.75.784 1.75 1.75s-.784 1.75-1.75 1.75-1.75-.784-1.75-1.75.784-1.75 1.75-1.75zm18.25-1.25c0-.414-.336-.75-.75-.75h-13.5c-.414 0-.75.336-.75.75s.336.75.75.75h13.5c.414 0 .75-.336.75-.75zm-18.25-3.75c.966 0 1.75.784 1.75 1.75s-.784 1.75-1.75 1.75-1.75-.784-1.75-1.75.784-1.75 1.75-1.75zm18.25-.25c0-.414-.336-.75-.75-.75h-13.5c-.414 0-.75.336-.75.75s.336.75.75.75h13.5c.414 0 .75-.336.75-.75zm-18.25-4.75c.966 0 1.75.784 1.75 1.75s-.784 1.75-1.75 1.75-1.75-.784-1.75-1.75.784-1.75 1.75-1.75zm18.25.75c0-.414-.336-.75-.75-.75h-13.5c-.414 0-.75.336-.75.75s.336.75.75.75h13.5c.414 0 .75-.336.75-.75z" fill-rule="nonzero"></path>`

let totalEntries = 0; // Инициализация счётчика для подсчёта общего числа заявок
let imageEntries = 0; // инициализация счётчика для подсчёта количества фото-заявок
let stepEntries = 0; // Инициализация счётчика для подсчёта заявок категории Step
let videoEntries = 0; // Инициализация счётчика для подсчёта видео-заявок

const getAllEntries = async (page) => {
    const entries = await page.$$("ul.grid li.contents");
    totalEntries += entries.length; // Инкрементирование общего числа заявок на их количество, обнаруженное на текущей странице

    for (const entry of entries) {
        const entryHtml = await entry.innerHTML();
        const a = await entry.$("a");
        const href = await a.getAttribute("href");

        if (entryHtml.includes(IMG_ICON)) {
            // Это неконкурентная фото-заявка
            imageEntries++; // Инкрементирование счётчика фото-заявок без вывода на экран
        } else {
            if (entryHtml.includes(VIDEO_ICON)) {
                console.log('Competitive video entry found');
                videoEntries++; // Инкрементирование счётчика видео-заявок
            } else if (entryHtml.includes(STEP_ICON)) {
                console.log('Competitive step entry found');
                stepEntries++; // Инкрементирование счётчика заявок Step
            } else {
                console.log('Unknown entry type');
            }
            console.log(href); // Вывод на экран href не-фото заявок для дальнейшего анализа
        }
    }

    const nextButton = await page.$('a[rel="next"]');
    if (nextButton) {
        await nextButton.click();
        await page.waitForResponse(response => response.status() === 200);
        // Ожидание несколько секунд, чтобы осуществить загрузку и исключить ограничение скорости передачи
        await page.waitForTimeout(3200);
        await getAllEntries(page); // Рекурсивный вызов
    } else {
      console.log('No next button found, reached the end');
			// Всё готово, теперь можно анализировать заявки и делать умозаключения
			console.log(`Total entries processed: ${totalEntries}`);
      console.log(`Image entries: ${imageEntries}`); // Вывод общего числа фото-заявок
      console.log(`Video entries: ${videoEntries}`); // Вывод общего числа видео-заявок
      console.log(`Step entries: ${stepEntries}`); // Вывод общего числа заявок Step
    }
}

В этой функции мы рекурсивно выполняем серию задач:

  1. Получаем количество потомков в элементе ul.grid li.contents (каждый потомок — это заявка на конкурс).
  2. Для каждого потомка:
    1. Получаем href, чтобы вывести его в консоль, если соответствующая заявка нам интересна.
    2. Определяем, какая SVG-иконка использовалась для представления заявки:
      1. Если это фото-заявка, игнорируем её, так как конкурентной угрозы она для нас не представляет.
  3. Инкрементируем счётчик соответствующего типа заявок:
    1. Ищем кнопку Next page a[rel="next"]
    2. Если она существует, кликаем, ожидаем загрузки следующей страницы и повторяем процесс с шага 1.
    3. Если кнопки нет, значит мы достигли конца всех заявок, и можно выводить результаты.

Я выполнил скрипт командой node index.js и получил в консоли такие результаты:

No next button found, reached the end
Total entries processed: 538
Image entries: 439
Video entries: 17
Step entries: 67

Из всех 538 заявок на конкурс 439 были определены как неконкурентные фото-заявки.

? 439/538=0.8159851301

То есть можно смело отбросить 81% всех заявок. Закон Парето. Естественно!
Теперь можно удалить эти 439 заявок из расчётов вероятности победы:

? 538−439=99 
Шансы случайно выиграть один приз составляют: n/(99+n)

Но при этом также не забываем, что по результатам конкурса будет вручено аж 29 призов.

▍ Качество по-прежнему важно


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

Поэтому, опираясь на всё перечисленное, я решил ненадолго представить себя в роли судьи. Если ваша заявка банально не отвечает моим критериям качества контента, то я смело отнесу её к проигравшим.

В итоге я просмотрел часть заявок из категорий видео и Step. Если заявка оказывалась недостаточно хороша, я копировал её заголовок и сохранял в массиве неконкурентных. Затем, когда в ходе перебора галереи обнаруживалась одна из этих бесперспективных заявок, я обозначал её как nonCompetitor.

// Проверка, содержит ли href известную неконкурентную заявку
const KNOWN_NON_COMPETITORS = ['imagine', 'some', 'not', 'so', 'great', 'entry', 'slugs', 'in', 'this', 'list', 'that', 'have', 'been', 'redacted', 'to', 'be', 'kind']
let nonCompetitorEntries = 0; // Инициализация счётчика неконкурентных заявок

const isNonCompetitor = KNOWN_NON_COMPETITORS.some(nonCompetitor => href.includes(nonCompetitor));

if (isNonCompetitor) {
  nonCompetitorEntries++; // Инкрементирование счётчика неконкурентных заявок
  continue; // Пропуск вывода в консоль и подсчёт этой заявки
}

▶️
Качество воспроизведения видеороликов из галереи заявок оставляло желать лучшего. Медленная загрузка неоптимизированных MP4, отсутствие миниатюрных превью, заурядный проигрыватель тегов <video> — всё это участники могли легко доработать с помощью таких сервисов, как Mux.

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

? 99−23=76
Шансы выиграть один приз: n/(76+n)

В случае регистрации одной заявки мои шансы на выигрыш $5 000 составляли 1 к 77.

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

Я обновил код вывода в консоль, чтобы он включал количество конкурентных заявок. Именно с этим числом мне предстояло сопоставлять все вероятности на победу:

console.log(`Total entries processed: ${totalEntries}`);
console.log(`Image entries: ${imageEntries}`); // Вывод общего числа фото-заявок
console.log(`Non-competitive entries: ${nonCompetitorEntries}`); // Вывод общего числа неконкурентных заявок
console.log(`Video entries: ${videoEntries}`); // Вывод общего числа видео-заявок
console.log(`Step entries: ${stepEntries}`); // Вывод общего числа заявок Step
const competitiveEntries = totalEntries - nonCompetitorEntries - imageEntries;

console.log(`Competitive entries: ${competitiveEntries}`); // Вывод общего числа конкурентных заявок

▍ Вычисление вероятности выигрыша нескольких призов


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

const NUMBER_OF_PERSONAL_ENTRIES = 8;

const prizeDistribution = [
    { award: "grand prize", quantity: 1, amount: "$5,000" },
    { award: "second place prize", quantity: 1, amount: "$3,000" },
    { award: "third place prize", quantity: 1, amount: "$1,500" },
    { award: "fourth place prize", quantity: 7, amount: "$500 Lowe's gift card" },
    { award: "fifth place prize", quantity: 6, amount: "$250 Lowe's gift card" },
    { award: "sixth place prize", quantity: 13, amount: "WD-40 Branded Yeti® Rambler® One Gallon Jug" }
];

Обновим вывод, чтобы он раскрывал больше информации о призах:

console.log(`Number of prizes: ${prizes.reduce((acc, curr) => acc + curr.quantity, 0)}`); // Вывод общего числа призов

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

Биномиальный что? Объяснение биномиальных коэффициентов по аналогии с банкой печенья
  1. Общее количество печенек (n): представьте, что у вас в банке лежит 25 печенек, каждая со своим типом. Вам нужно взять несколько из них, чтобы угостить друзей.
  2. Выбор печенек (k): вы решаете достать из банки 5 печенек. Порядок из извлечения значения не имеет. Вы просто хотите знать, сколько групп по 5 печенек вы можете собрать из всех 25 доступных.
  3. Комбинации (биномиальный коэффициент): биномиальный коэффициент (или число сочетаний из n по k) — это способ вычисления количества групп печенек, которые вы можете сформировать. Он показывает, сколько уникальных комбинаций по 5 печенек вы можете собрать из 25 доступных.

Детальный разбор:

  1. Одна печенька (k=1): если вы хотите извлечь всего одну печеньку (k=1), то ясно, что вам становится доступно 25 разных вариантов (по одному для каждой печеньки).
  2. Две печеньки (k=2): если вам нужно взять 2 печеньки, биномиальный коэффициент поможет определить все пары, которые вы можете собрать. Это будет уже сложнее, поскольку простое умножение здесь использовать нельзя. Нужно также учесть тот факт, что выбор печеньки A и затем печеньки B будет равноценен выбору сначала печеньки B и затем печеньки A.
  3. Общий случай (k печенек): для любого числа печенек k, которое вы хотите взять, биномиальный коэффициент позволит вычислить общее число уникальных групп из этих k печенек, которые можно собрать из их общего числа n.

Математические шаги (переведённые в аналогию):

  1. Стартовая точка: начинаем с n печенек и выбираем первую, получаем n вариантов.
  2. Следующие варианты: для следующей печеньки у нас есть n−1 вариантов и так далее, пока не будут выбраны все k печенек.
  3. Избегание двойного подсчёта: чтобы исключить многократный подсчёт одной группы в разном порядке мы производим деление по количеству способов, которыми можно упорядочить эти k печенек среди них самих (то есть среди k).

Объяснение кода:

В коде биномиальный коэффициент вычисляется так:

function binomialCoefficient(n, k) {
    if (k > n) return 0; // Вы не можете выбрать больше печенек, чем у вас есть
    if (k === 0 || k === n) return 1; // Если вы не выбираете печеньки или выбираете их все, то сделать это можно только одним способом

    let coefficient = 1;
    for (let i = 1; i <= k; i++) {
        coefficient *= (n - (k - i)); // Умножение на количество оставшихся вариантов
        coefficient /= i; // Деление на число способов упорядочивания выбранных печенек
    }
    return coefficient;
}

  • Инициализация: начинаем с коэффициента 1.
  • Цикл: на каждом шаге, начиная с 1 и до k, обновляем коэффициент:
    • Умножаем на количество оставшихся вариантов.
    • Делим на количество шагов, чтобы учесть различные вариации последовательности.

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

Я был вполне уверен, что на основе достаточно продуманного промпта GPT4o сможет составить код для вычисления моих шансов на победу:

I am trying to understand my odds of winning a contest. The contest allows me to submit multiple entries. The judges are selecting a total of 29 winners. there is one first place winner, one second place winner, one third place winner, seven fourth place winners, six fifth place winners, and 13 sixth place winners. I'd like to learn my chances to win based on if i submit n number of entries. i also know the total number of existing submissions, so I can pass that number as an argument to the function provide the javascript code to help me calculate my chances of winning. I want to provide the number of my entries, the number of competing entries, and learn my odds of winning.
Перевод
Я пытаюсь понять свои шансы на победу в конкурсе. Условия конкурса позволяют прислать несколько заявок. Всего судьи выберут 29 победителей. Из них будет один победитель на первое место, один на второе, один на третье, семь на четвёртое, шесть на пятое и тринадцать на шестое. Я хочу узнать свои шансы на победу в случае, если отправлю n заявок. Мне также известно общее число присланных на конкурс заявок, и я могу передать это число в виде аргумента в функцию. Создай мне код JavaScript для вычисления моих шансов на победу. Я хочу передать количество моих заявок, количество конкурирующих заявок и выяснить свои шансы на победу.

На это GPT4o сгенерировал такой код:

function binomialCoefficient(n, k) {
    if (k > n) return 0;
    if (k === 0 || k === n) return 1;

    let coefficient = 1;
    for (let i = 1; i <= k; i++) {
        coefficient *= (n - (k - i));
        coefficient /= i;
    }
    return coefficient;
}

// Функция для вычисления вероятности выигрыша ровно k призов
function probabilityOfWinningKPrizes(totalEntries, yourEntries, totalPrizes, k) {
    if (k > yourEntries || k > totalPrizes) return 0;

    let otherEntries = totalEntries - yourEntries;
    let waysForYouToWinKPrizes = binomialCoefficient(yourEntries, k);
    let waysForOthersToWinRemainingPrizes = binomialCoefficient(otherEntries, totalPrizes - k);
    let totalWaysToAwardPrizes = binomialCoefficient(totalEntries, totalPrizes);

    let probability = (waysForYouToWinKPrizes * waysForOthersToWinRemainingPrizes) / totalWaysToAwardPrizes;
    return probability;
}

// Функция для преобразования вероятности в понятный человеку показатель шансов
function probabilityToOdds(probability) {
    if (probability === 0) return "0 (impossible)";
    let odds = 1 / probability;
    return `1 in ${Math.round(odds).toLocaleString()}`;
}

// Функция для вычисления подробных вероятностей выигрыша призов в каждой категории
function calculateDetailedProbabilities(totalEntries, yourEntries, prizeDistribution) {
    let detailedProbabilities = {};

    prizeDistribution.forEach(prize => {
        let probabilities = [];
        let humanReadableOdds = [];
        let totalProbability = 0;
        for (let k = 0; k <= prize.quantity; k++) {
            let probability = probabilityOfWinningKPrizes(totalEntries, yourEntries, prize.quantity, k);
            probabilities.push(probability);
            totalProbability += probability;
            humanReadableOdds.push(probabilityToOdds(probability));
        }
        console.log(`Total probability for ${prize.award} should be close to 1: ${totalProbability}`);
        detailedProbabilities[prize.award] = {
            probabilities,
            humanReadableOdds
        };
    });

    return detailedProbabilities;
}

// Функция для вычисления кумулятивной вероятности выигрыша хотя бы одного приза
function calculateCumulativeProbability(totalEntries, yourEntries, prizeDistribution) {
    let probabilityOfNotWinningAnything = 1;

    prizeDistribution.forEach(prize => {
        let probabilityOfNotWinningThisPrize = probabilityOfWinningKPrizes(totalEntries, yourEntries, prize.quantity, 0);
        probabilityOfNotWinningAnything *= probabilityOfNotWinningThisPrize;
    });

    return 1 - probabilityOfNotWinningAnything;
}

// Функция для вычисления объединённых вероятностей выигрыша во всех категориях призов
function calculateCombinedProbabilities(totalEntries, yourEntries, totalPrizes) {
    let combinedProbabilities = [];
    let combinedHumanReadableOdds = [];
    let totalProbability = 0;

    for (let k = 0; k <= totalPrizes; k++) {
        let probability = probabilityOfWinningKPrizes(totalEntries, yourEntries, totalPrizes, k);
        combinedProbabilities.push(probability);
        totalProbability += probability;
        combinedHumanReadableOdds.push(probabilityToOdds(probability));
    }

    console.log(`Total combined probability for all prizes should be close to 1: ${totalProbability}`);
    return { combinedProbabilities, combinedHumanReadableOdds };
}

Неплохо.

Теперь же, когда у нас есть вспомогательные функции, используем их для вывода различных вероятностей и шансов в случае отправки на конкурс различного числа заявок:

// Пример
const totalEntries = 76;
const yourEntries = 8; // Измените это значение, чтобы проверить вероятности при различном числе отправленных заявок
const prizeDistribution = [
    { award: 'First place', quantity: 1 },
    { award: 'Second place', quantity: 1 },
    { award: 'Third place', quantity: 1 },
    { award: 'Fourth place', quantity: 7 },
    { award: 'Fifth place', quantity: 6 },
    { award: 'Sixth place', quantity: 13 }
];
const totalPrizes = prizeDistribution.reduce((acc, prize) => acc + prize.quantity, 0);
const totalEntriesIncludingYours = totalEntries + yourEntries;

let detailedProbabilities = calculateDetailedProbabilities(totalEntriesIncludingYours, yourEntries, prizeDistribution);

// Подробное отображение вероятностей и шансов на победу в понятном человеку виде
Object.keys(detailedProbabilities).forEach(prize => {
    let prizeInfo = detailedProbabilities[prize];
    console.log(`Your probabilities of winning 0 to ${prizeDistribution.find(p => p.award === prize).quantity} ${prize} prizes are:`, prizeInfo.probabilities);
    prizeInfo.humanReadableOdds.forEach((odds, index) => {
        console.log(`The odds of winning exactly ${index} ${prize} prizes are: ${odds}`);
    });
});

// Вычисление общей вероятности выигрыша хотя бы одного приза
let cumulativeProbability = calculateCumulativeProbability(totalEntriesIncludingYours, yourEntries, prizeDistribution);
console.log("Your cumulative probability of winning at least one prize is:", cumulativeProbability);

// Вычисление общих вероятностей для всех призов
let combinedResults = calculateCombinedProbabilities(totalEntriesIncludingYours, yourEntries, totalPrizes);

// Вывод общих вероятностей и шансов на победу в понятном человеку виде
console.log(`Your probabilities of winning 0 to ${totalPrizes} prizes across all categories are:`, combinedResults.combinedProbabilities);
combinedResults.combinedHumanReadableOdds.forEach((odds, index) => {
    console.log(`The odds of winning exactly ${index} prizes across all categories are: ${odds}`);
});

Итак, барабанная дробь…

Итоговые расчёты показывали, что при отправке 8 заявок у меня был более высокий шанс выиграть 1, 2, 3, 4 или даже 5 призов, чем не выиграть вообще. При этом была 95% вероятность, что я выиграю как минимум один приз.

Total probability for First place should be close to 1: 1
Total probability for Second place should be close to 1: 1
Total probability for Third place should be close to 1: 1
Total probability for Fourth place should be close to 1: 1
Total probability for Fifth place should be close to 1: 1
Total probability for Sixth place should be close to 1: 1
Your probabilities of winning 0 to 1 First place prizes are: [ 0.9047619047619048, 0.09523809523809523 ]
The odds of winning exactly 0 First place prizes are: 1 in 1
The odds of winning exactly 1 First place prizes are: 1 in 11
Your probabilities of winning 0 to 1 Second place prizes are: [ 0.9047619047619048, 0.09523809523809523 ]
The odds of winning exactly 0 Second place prizes are: 1 in 1
The odds of winning exactly 1 Second place prizes are: 1 in 11
Your probabilities of winning 0 to 1 Third place prizes are: [ 0.9047619047619048, 0.09523809523809523 ]
The odds of winning exactly 0 Third place prizes are: 1 in 1
The odds of winning exactly 1 Third place prizes are: 1 in 11



Вероятности выигрыша приза за четвёртое место
Your probabilities of winning 0 to 7 Fourth place prizes are: [
  0.48267009292649365,
  0.3861360743411949,
  0.11420926142486047,
  0.01586239742011951,
  0.0010864655767205144,
  0.000035236721407151816,
  4.6982295209535756e-7,
  1.7662516996066073e-9
]
The odds of winning exactly 0 Fourth place prizes are: 1 in 2
The odds of winning exactly 1 Fourth place prizes are: 1 in 3
The odds of winning exactly 2 Fourth place prizes are: 1 in 9
The odds of winning exactly 3 Fourth place prizes are: 1 in 63
The odds of winning exactly 4 Fourth place prizes are: 1 in 920
The odds of winning exactly 5 Fourth place prizes are: 1 in 28,379
The odds of winning exactly 6 Fourth place prizes are: 1 in 2,128,461
The odds of winning exactly 7 Fourth place prizes are: 1 in 566,170,722


Вероятности выигрыша приза за пятое место
Your probabilities of winning 0 to 6 Fifth place prizes are: [
  0.5378323892609501,
  0.3636049955566986,
  0.0883762141978087,
  0.009685064569622872,
  0.0004907971910281861,
  0.00001047034007526797,
  6.888381628465768e-8
]
The odds of winning exactly 0 Fifth place prizes are: 1 in 2
The odds of winning exactly 1 Fifth place prizes are: 1 in 3
The odds of winning exactly 2 Fifth place prizes are: 1 in 11
The odds of winning exactly 3 Fifth place prizes are: 1 in 103
The odds of winning exactly 4 Fifth place prizes are: 1 in 2,038
The odds of winning exactly 5 Fifth place prizes are: 1 in 95,508
The odds of winning exactly 6 Fifth place prizes are: 1 in 14,517,198


Вероятности выигрыша приза за шестое место
Your probabilities of winning 0 to 13 Sixth place prizes are: [
  0.2440438148568602,
  0.3965711991423978,
  0.2562460055997032,
  0.08541533519990106,
  0.015935696865653184,
  0.00168730907989269,
  0.00009781501912421392,
  0.0000027947148321203974,
  2.952163555056758e-8,
  0,
  0,
  0,
  0,
  0
]
The odds of winning exactly 0 Sixth place prizes are: 1 in 4
The odds of winning exactly 1 Sixth place prizes are: 1 in 3
The odds of winning exactly 2 Sixth place prizes are: 1 in 4
The odds of winning exactly 3 Sixth place prizes are: 1 in 12
The odds of winning exactly 4 Sixth place prizes are: 1 in 63
The odds of winning exactly 5 Sixth place prizes are: 1 in 593
The odds of winning exactly 6 Sixth place prizes are: 1 in 10,223
The odds of winning exactly 7 Sixth place prizes are: 1 in 357,818
The odds of winning exactly 8 Sixth place prizes are: 1 in 33,873,462
The odds of winning exactly 9 Sixth place prizes are: 0 (impossible)
The odds of winning exactly 10 Sixth place prizes are: 0 (impossible)
The odds of winning exactly 11 Sixth place prizes are: 0 (impossible)
The odds of winning exactly 12 Sixth place prizes are: 0 (impossible)
The odds of winning exactly 13 Sixth place prizes are: 0 (impossible)


Суммирование вероятностей
Your cumulative probability of winning at least one prize is: 0.953078912795086
Total combined probability for all prizes should be close to 1: 1.0000000000000002
Your probabilities of winning 0 to 29 prizes across all categories are: [
    0.02792894331261447,   0.13498989267763659,
     0.2699797853552732,   0.29157816818369503,
    0.18580961697980572,   0.07146523729992528,
   0.016180808445266093, 0.0019690930912228577,
  0.0000984546545611429,                     0,
                      0,                     0,
                      0,                     0,
                      0,                     0,
                      0,                     0,
                      0,                     0,
                      0,                     0,
                      0,                     0,
                      0,                     0,
                      0,                     0,
                      0,                     0
]
The odds of winning exactly 0 prizes across all categories are: 1 in 36
The odds of winning exactly 1 prizes across all categories are: 1 in 7
The odds of winning exactly 2 prizes across all categories are: 1 in 4
The odds of winning exactly 3 prizes across all categories are: 1 in 3
The odds of winning exactly 4 prizes across all categories are: 1 in 5
The odds of winning exactly 5 prizes across all categories are: 1 in 14
The odds of winning exactly 6 prizes across all categories are: 1 in 62
The odds of winning exactly 7 prizes across all categories are: 1 in 508
The odds of winning exactly 8 prizes across all categories are: 1 in 10,157
The odds of winning exactly 9 prizes across all categories are: 0 (impossible)
The odds of winning exactly 10 prizes across all categories are: 0 (impossible)
The odds of winning exactly 11 prizes across all categories are: 0 (impossible)
The odds of winning exactly 12 prizes across all categories are: 0 (impossible)
The odds of winning exactly 13 prizes across all categories are: 0 (impossible)
The odds of winning exactly 14 prizes across all categories are: 0 (impossible)
The odds of winning exactly 15 prizes across all categories are: 0 (impossible)
The odds of winning exactly 16 prizes across all categories are: 0 (impossible)
The odds of winning exactly 17 prizes across all categories are: 0 (impossible)
The odds of winning exactly 18 prizes across all categories are: 0 (impossible)
The odds of winning exactly 19 prizes across all categories are: 0 (impossible)
The odds of winning exactly 20 prizes across all categories are: 0 (impossible)
The odds of winning exactly 21 prizes across all categories are: 0 (impossible)
The odds of winning exactly 22 prizes across all categories are: 0 (impossible)
The odds of winning exactly 23 prizes across all categories are: 0 (impossible)
The odds of winning exactly 24 prizes across all categories are: 0 (impossible)
The odds of winning exactly 25 prizes across all categories are: 0 (impossible)
The odds of winning exactly 26 prizes across all categories are: 0 (impossible)
The odds of winning exactly 27 prizes across all categories are: 0 (impossible)
The odds of winning exactly 28 prizes across all categories are: 0 (impossible)
The odds of winning exactly 29 prizes across all categories are: 0 (impossible)




Я определённо участвую в этом конкурсе!

Недолго думая, я сгонял в ближайший хозяйственный магазин и купил банку WD-40, которая обошлась мне в $7.87.



▍ Высокое качество при малых трудозатратах


В свете стремительно приближавшегося дедлайна (и многих других очевидных приоритетов) лишнего времени у меня не было. К счастью, починка скрипучих шарниров, заевших раздвижных дверей и капризного дверного замка не требовала серьёзных временных вложений. Весь процесс исправления этих мелких недочётов моего жилища 1930 года постройки я снял на свой iPhone 13.

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

В конечном итоге за утро я успел отснять 8 роликов, на каждый из которых потратил от 15 до 20 минут:

  1. Очистка почтового ящика.
  2. Возвращение подвижности дверному замку.
  3. Смазка салазок раздвижной двери.
  4. Устранение скрипа в дверном шарнире.
  5. Удаление старой сервисной наклейки с отопительного котла.
  6. Смазка рычага системы HVAC (ОВиК, отопление, вентиляция и кондиционирование).
  7. Стирание следов перманентного маркера с художественного картона.
  8. Защита домика феи, обустроенного в древесном пне.

?При создании видеоконтента я пользуюсь простым правилом. стараясь всегда показать задачу под необычным углом. Очень близкая съёмка, нестандартная точка обзора и прочие приёмы композиции прекрасно помогают создать интересное видео.



Ещё фото








Теперь, когда все видеоматериалы были готовы, пришло время монтажа. Я закинул все видео в Adobe Premiere и взял из них отдельные кадры, которые лучше всего демонстрировали процесс починки.

▍ Схема


Каждое видео монтировалось по общей схеме:

  1. Представление проблемы.
  2. Утверждение, что проблему можно исправить с помощью WD-40 и простых домашних инструментов.
  3. Подробное описание шагов, которые потребуются для решения проблемы.
  4. Демонстрация предмета после его починки с помощью WD-40.

Для предыдущих конкурсов мне постоянно приходилось сначала составлять, а потом записывать дикторский текст с помощью микрофона, запинаясь на некоторых строчках и повторяя запись, чтобы исключить случайный лай собаки или плач ребёнка.

▍ Elevenlabs


Инструмент Elevenlabs существенно упростил процесс создания дикторского текста. Я написал простой скрипт озвучки, соответствующий схеме каждой задачи, выбрал голос из библиотеки Elevenlabs и вставил этот скрипт в механизм генерации речи.



Чаще всего результат получался идеальным с первого раза. Из-за некорректного произнесения или странных ударений повторно сгенерировать пришлось всего несколько скриптов.


Прослушать запись можно в оригинале статьи

Полученную закадровую речь я скачивал в формате MP3 и вставлял на временную шкалу Premiere, финишно подстраивая под неё видеопоток ролика.

▍ Suno


Последним недостающим элементом была фоновая музыка. Обычно я искал подходящий саундтрек в архивах Creative Commons, но на этот раз инструменты ИИ до смешного упростили задачу.

Suno даёт возможность конкретно указать нужный мне мотив. После нескольких генераций мне удалось получить идеальный трек в стиле HGTV, ставший прекрасным дополнением для моих видео.

soft drums, electric guitar, folk, acoustic, banjo, instrumental

Весь монтаж занял ~8 часов, после чего мои заявки были готовы к отправке!


Этот и другие ролики доступны для просмотра в оригинале статьи

▍ E-mail


Итак, 24 июля 2024 года в 16:47, а именно через 3 часа и 47 минут после указанного в условиях Winner Notification дедлайна*, я получил это письмо:



*Во всех случаях официальным устройством для отслеживания времени выступает компьютер администратора (согласно правилам конкурса). Должно быть, запоздание вызвано некоторой рассинхронизацией во времени.

Семь из восьми отправленных мной заявок выиграли призы.



▍ Вопросы, которые у вас могут возникнуть


Что случилось с заявкой, которая ничего не выиграла?
В заявке №8 я немного отклонился от своей схемы и решил сделать ролик более кинематографичным, сюжетным и весёлым, чтобы он мог претендовать на главный приз. В итоге он стал моим фаворитом, но по факту ничего не выиграл. Это прекрасно показывает, что при оценке по взвешенным критериям ваша креативность не учитывается (если только она не прописана отдельным пунктом).

Вы расстроены, что не заняли 1, 2 и даже 3 место?
Немного, но всё же думаю, что судьи были объективны, и я получил те награды, которые реально заслужил. Ни один из присланных мной проектов не был особо интересным или новаторским.


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

…Почему?
Потому что писать важно. Это моё увлечение, и я хочу запомнить, насколько нелепым был этот период в моей жизни.

Мне нравится ваш стиль письма. Поможете мне усовершенствовать мой?

На что вы потратите выигрыш?
Даже не знаю. Пока что подумываю купить машину для приготовления мороженого. Если у вас есть какие-то хорошие идеи, поделитесь ими.

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


Telegram-канал со скидками, розыгрышами призов и новостями IT ?

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


  1. Irbisx
    08.09.2024 10:14
    +2

    Почему-то ожидал, что автор отправит создателям конкурса формулу с расчетами, что именно он должен получить первый приз, согласно его (автора) выкладкам.


  1. tmaxell
    08.09.2024 10:14

    Достойный перевод отличного кейса, автору спасибо