Функція гарантована, щоб ніколи не повертати одне і те ж значення двічі [закрито]


23

Це питання, яке мені задали на співбесіді, і я не можу зрозуміти відповіді, яку вони шукали, тому сподіваюся, що хтось тут може мати якісь ідеї. Мета - написати функцію, яка гарантовано ніколи не поверне одне і те ж значення двічі. Припустимо, що до цієї функції будуть доступні декілька машин одночасно.

Моя ідея полягала в тому, щоб призначити кожній машині унікальний ідентифікатор і передати це значення в унікальну функцію генератора значень:

var i = 0;
function uniq(process_id, machine_id) {
   return (i += 1).toString() + machine_id + "-" + process_id;
}

Це дозволить уникнути випадання в умовах перегонів, оскільки навіть якщо два чи більше процесів читають одне і те ж значення i, кожне повернене значення позначається унікальною комбінацією ідентифікатора процесу та ідентифікатора машини. Однак моєму інтерв'юєру ця відповідь не сподобалася, оскільки приведення іншої машини в Інтернет означає присвоєння їй ідентифікатора.

Тож чи може хтось придумати інший спосіб вирішити це питання, який не передбачає налаштування кожної машини на унікальний ідентифікатор? Я хотів би отримати відповідь у випадку, якщо це запитання виникне знову. Спасибі.


31
Гарантується в суворому розумінні цього слова? Я маю на увазі, навіть Посібники в якийсь момент почнуть себе повторювати. Ми можемо не жити більше, але гарантує .. І, до речі, ідентифікатор процесу далеко не унікальний .
JensG

7
@CodesInChaos - Це досить жахливе припущення, враховуючи, що в деяких операційних системах тривіально змінювати вашу mac-адресу.
Теластин

7
"Припустимо, що до цієї функції будуть доступні кілька машин одночасно" - якщо чесно, це може означати "код працює на кожній машині побічно, без зв'язку між машинами", або "є центральна машина / центральна база даних, де функція надається для інших машин, доступних по мережі ". Спершу слід почати уточнювати це.
Док Браун

28
Це було хитромудрі питання? Наприклад, функція, що містить нескінченний цикл, ніколи не поверне одне і те ж значення двічі ..
Брендан

8
Можливо, вони шукали програміста, який задаватиме питання щодо сумнівних вимог, а не робити припущення та працювати з ним :)
theMayer

Відповіді:


60

Не майте фантазії, просто киньте простий лічильник (безпечний для потоків) за деякою кінцевою точкою зв’язку (WCF, веб-сервіс, що завгодно):

   long x = long.MinValue;
   public long ID(){
       return Interlocked.Increment(ref x);
   }

Так, вона з часом переповниться. Так, він не обробляє перезавантаження. Так, це не випадково. Так, хтось може це запустити на декількох серверах.

Це найпростіша річ, яка відповідає практичним вимогам. Тоді нехай вони будуть тими, хто вирішує ці проблеми (щоб переконатися, що вони розуміють обмеження, чи дійсно вони вважають, що вам потрібно більше 2 ^ 64 ід), тож ви можете запитати про те, які компроміси в порядку. Чи потрібно переживати перезавантаження? Що з відмовою на жорсткому диску? А як щодо ядерної війни? Чи потрібно це бути випадковим? Як випадково?


7
Це хороша відповідь, оскільки інтерв'юер ніколи не задає питань, щоб отримати пряму відповідь. Вони хочуть, щоб ви дали відповідь, де ви можете виправдати свої рішення. Якщо ви розумієте домен, майже будь-яка відповідь буде придатною, якщо ви зможете це виправдати.

7
Як це має працювати, якщо код працює на різних машинах (так очевидно, в різних процесах)? Кожен процес матиме різні копії x. І я думаю, що без пояснень того, який механізм блокування ви маєте на увазі, ця відповідь є досить невиразною.
Doc Brown

7
@DocBrown "доступ до декількох машин одночасно", мабуть, означає, що кілька машин отримують доступ до однієї функції на одному сервері. В іншому випадку слід сказати: "Кілька машин одночасно запускають копію цієї функції"
Falco

3
@LightnessRacesinOrbit: Я думаю, це мається на увазі C # і System.Threading.Interlockedклас, який забезпечує атомні прирости. Але ви можете це також читати як якийсь псевдо-код.
Doc Brown

3
Якби я запитував мене, я був би дуже незадоволений цією пропозицією. Починати щось реалізовувати, навіть не знаючи, які вимоги - це великий червоний прапор. Я очікував би, що ви запитаєте.
JensG

25

Якби мені було задано це питання, і вони дали зрозуміти, що воно повинно бути унікальним у перезавантаженнях і на різних машинах, я б дав їм функцію, яка запускає стандартний механізм для створення нового GUID, що б там не сталося мова, що використовується.


Проблема з v4 GUID полягає в тому, що вони є лише дуже ймовірними унікальними, не гарантованими унікальними. На практиці це не велике питання, але не задовольняє вимогам, якщо інтерв'юер сприймає їх буквально.
CodesInChaos

Зокрема, якщо стандартний механізм GUID не відповідає вимогам інтерв'юера, тоді обмацуйте відмінності в вимогах між інтерв'юером та звичайним користувачем GUID. Розумний інтерв'юер ставить цей вид питання ( «Як поживає <деякі взагалі відому стандартної річ , можливо , з невеликою зміною від звичайних вимог>») слід очікувати дуже різні видів відповіді від кандидатів , які знають про стан мистецтва для GUID та кандидатів, які вигадують щось з нуля.
Стів Джессоп

Це, мабуть, найпростіша відповідь, припускаючи гнучкі вимоги.
theMayer

9
+1, оскільки це в основному проблема, яку вирішують керівники. Випуск дубліката Guid, незалежно від його формату, - найскладніша лотерея на планеті. Мабуть, багато людей не мають сенсу для експоненціальної ймовірності зіткнень.
usr

3
О, і якщо ви пропонуєте відповісти на будь-яке подібне запитання "використовувати стандартну функцію", очікуйте подальшого запитання "і як реалізується стандартна функція?". На що ви можете дуже відповісти: "Я не знаю, але я, безумовно, шукаю це, а не намагаюся щось вигадати", що є абсолютно точною відповіддю, яка повністю не зможе зберегти очікувану зупинку невір'я в умовах співбесіди, що ви коли-небудь зробите щось важливе, не досліджуючи це спочатку ;-)
Стів Джессоп

22

Інтерв'юер сказав, що метод буде називатися одночасно, а не паралельно; просто поверніть дату / час до стільки знаків після коми.

Чому всі переосмислюють це? Ви будете мертві довгий час, перш ніж буде витрачено будь-яку кінцевість, і у вас немає шансів зіткнення.

Якщо ви турбуєтесь про те, щоб повернутися в той же час, додайте затримку на найменшу кількість вимірюваного часу.

Якщо ви турбуєтесь про те, щоб повернути годинник на літній час (випробовуєте 1 раз двічі), додайте константу до часу, коли ви вдруге відчуєте це.


12
Або просто повернути час UTC незалежно від часового поясу запитувачів. Оскільки UTC не локалізований, то на нього не будуть впливати зміни DST.
Мауро

1
System.currentTimeNanos () :-)
Falco

1
Якщо ви не повертаєте дату та час у форматі, читаному людиною, у вашому значенні все одно не повинно бути інформації про часовий пояс.
Гонки легкості з Монікою

12
Найменша кількість часу все ще збирається створити зіткнення, якщо їх викликають досить часто / одночасно. Це також спричинить зіткнення через дрейф синхронізації годин, шкідливі маніпулювання годинником, а якщо ви не обережні - економія денного світла.
Теластин

1
Дуже креативно, принаймні. Опираючись на годинник, який час від часу буде коригуватися, все ще не така чудова ідея, ІМХО. Зсув не вбереже вас від зіткнень.
JensG

15

По-перше, вам потрібно буде задати інтерв'юєру два питання.


Питання 1.

чи інтерв'юер очікує, що одна або декілька "центральних машин" будуть використані для присвоєння деяких унікальних номерів або блоків унікальних номерів.


Питання 2.

Чи очікує інтерв'ю механізм виявлення зіткнень, чи замість цього прийме розрахований ризик мінусового шансу зіткнення без явного їх виявлення.

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


Існує неявне запитання 3, ...

Але це вам доведеться оцінити себе, не запитуючи, тому що запитувати свого інтерв'ю вкрай неввічливо.

Будь інтерв'юер припускає знання ймовірності, ризику та деяких простих прийомів, застосовуваних у криптографічних та інформаційно-захисних системах.

Перший вид знань гарантує, що ви не намагаєтесь переконати ненаукову людину у прийнятті наукової концепції, яку вони не приймуть.

Другий вид знань гарантує вирішення проблем, які є додатковою лише ймовірністю. Іншими словами, як захиститись від "нападників", які хочуть навмисно порушити вашу схему рандомізації, маніпулюючи машиною (машинами) або їх віртуальними хостами, щоб змусити дві машини генерувати однакове значення.


Навіщо питати.

Причина полягає в тому, що якщо інтерв'юер так чи інакше очікує, спроба відповісти протилежним підходом ніколи не зробить інтерв'юера щасливим.

Більш глибока причина полягає в тому, що деяким людям не подобається ідея сказати - 1.0e-20шанс провалитися. (Я постараюся тут не розпалювати філософські чи релігійні аргументи.)


Перш за все, "простір імен" випадкових чисел перетворюється на ієрархію, при цьому певна кількість бітів виділяється одному джерелу рандомізації, а інша кількість бітів, виділених якимись іншими способами тощо.

Централізований підхід покладається на деякий центральний орган, щоб однозначно призначити біт першого рівня. Потім інші машини можуть заповнити решту шматочків.

Існує кілька децентралізованих підходів:

  • Просто генеруйте випадкові числа настільки добре, наскільки це могло, і прийміть практично нульовий шанс не виправданих обчислень.
  • Використовуйте криптографічні засоби для генерування випадкових значень з детермінованого джерела, скажімо, збільшення значень.

Я думаю, що це найкраща відповідь. Інші - рішення без вимог.
Джек Едлі

Зауважуючи своє третє запитання - здається, що компетентність є безпечним припущенням або, принаймні, нерелевантним. Якщо компанія не запропонувала компетентного інтерв'ю, то, ймовірно, будуть більші недоліки у процесі відбору. Якщо вони це зробили, то він / вона оцінить питання.
theMayer

1
Чому не можна було б вирішити "питання 3", запитавши щось, "Чи потрібна нам справді гарантована унікальність або просто дуже і дуже низька ймовірність зіткнень?" і "Наскільки це потрібно забезпечити? Чи потрібно припускати, що зловмисник намагатиметься зламати механізм? Які види атак нас турбують?" Відповіді на ці запитання повинні уточнювати, чи розуміючий це розуміє ці питання і що вони очікують.
jpmc26

12

Отже, маючи на увазі, що це питання інтерв'ю, а не фактичний сценарій реального життя, я вважаю, що правильним підходом (і, можливо, те, що шукає інтерв'юер) є задати уточнююче запитання або написати "Це не може робиться "і рухатися далі. Ось чому.

Що запитує інтерв'юер:

Напишіть функцію, яка гарантовано ніколи не поверне одне і те ж значення двічі. Припустимо, що до цієї функції будуть доступні кілька машин одночасно.

Що потрібно інтерв'юєру:

Чи ефективно оцінює цей кандидат вимоги та чи потребує додаткового внеску, коли це вимагається?

Ніколи не припускайте.

Коли інженеру вручається вимога (через SOW або Специфікацію або якийсь інший документ із вимогами), деякі є само собою зрозумілими, а інші - абсолютно незрозумілими. Це прекрасний приклад останнього. Як показали попередні відповіді, немає можливості відповісти на цю вимогу, не роблячи декількох основних припущень або (a) щодо природи питання або (b) щодо природи системи, оскільки вимога не може бути виконана як написано (неможливо).

Більшість відповідей робить ту чи іншу спробу вирішити проблему за допомогою ряду припущень. Спеціально рекомендується просто зробити це швидко і дозволити клієнту хвилюватися з цього приводу, якщо він неправильний.

Це дійсно поганий підхід. Як замовник, якщо я даю незрозумілу вимогу, а інженер відходить і розробляє мені рішення, яке не працює, я буду засмучуватися, що вони пішли на роботу і витратили мої гроші, не намагаючись спочатку запитати мене. Таке прийняття рішень кавалера свідчить про відсутність роботи в команді, невміння критично мислити та погану оцінку. Це може призвести до будь-яких негативних наслідків, включаючи втрату життя в критичній системі безпеки.

Чому задавати питання?

Справа, якщо ця вправа полягає в тому, що будувати дорого і забирати багато часу потрібно до неоднозначних вимог. У випадку з ОП вам поставили неможливе завдання. Вашою першою дією має стати запит на роз'яснення - що саме потрібно? Який ступінь унікальності потрібен? Що станеться, якщо значення не унікальне? Відповідь на ці запитання може бути різницею між кількома тижнями часу і кількома хвилинами. У реальному світі одним із найбільших рушійників витрат у складних системах (включаючи багато програмних систем) є нечіткі та недостатньо зрозумілі вимоги. Це призводить до дорогих і витратних за часом помилок, перепроектувань, розчарування клієнтів та команд, а також невмілого висвітлення засобів масової інформації, якщо проект досить великий.

Що відбувається, коли ти припускаєш?

З огляду на мій досвід роботи в аерокосмічній галузі та через дуже помітний характер аварійно-космічних збоїв, я люблю наводити приклади з цього домену, щоб проілюструвати важливі моменти. Розглянемо пару невдалих місій на Марсі - Марсовий кліматичний орбітер та Марський полярний десант. Обидві місії зазнали невдачі через проблеми із програмним забезпеченням - тому що інженери зробили неправдиві припущення, частково, через незрозумілі та погано передані вимоги.

Марсовий кліматичний орбіт - цей випадок, як правило, цитується як те, що відбувається, коли NASA намагається перетворити англійську мову на метричні одиниці. Однак це надмірно спрощена та погана репрезентація того, що насправді вийшло. Правда, виникла проблема перетворення, але це було пов’язано з погано переданими вимогами на етапі проектування та неправильною схемою перевірки / перевірки. Крім того, коли двоє різних інженерів помітили проблему, оскільки це було очевидно з даних про траєкторію польоту, вони не підняли проблему до належного рівня, оскільки вважали, що це помилка передачі. Якби команда операторів місії була б обізнана з цим питанням, був би достатній час для її виправлення та збереження місії. У цьому випадку виникла неможлива логічна умова, яка не була визнана тим, що це було, що призвело до дорогої відмови місії.

Марс Полярний десант- цей випадок трохи менш відомий, але, можливо, більш неприємний через його тимчасової близькості до провалу Марс Клімат Орбітер. У цій місії програмне забезпечення контролювало спускання ракети на землю Марсія. У точці на 40 метрів над поверхнею ноги земляного судна розгортаються, готуючись до посадки. На ногах також був датчик, який виявляв рух (сигналізував, коли вони вплинули), щоб повідомити програмному забезпеченню, щоб вимкнути двигун. Найкраща здогадка НАСА щодо того, що сталося (оскільки існує безліч можливих збоїв та неповних даних) полягає в тому, що випадкові коливання в ногах внаслідок їх розгортання одночасно і неправильно спрацьовують механізм відключення на 40 м над поверхнею, в результаті чого відбувається аварія та знищення 110 доларів США М космічний корабель. Ця можливість була піднята в процесі розвитку, але ніколи не звертався. Зрештою, команда програмного забезпечення зробила неправдиві припущення про те, як цей код потрібно запустити (одне з таких припущень полягає в тому, що помилковий сигнал був би занадто короткочасним, щоб його можна було сприймати, незважаючи на тести, що показували протилежне), і ці припущення ніколи не ставили під сумнів до після факт.

Додаткові міркування

Інтерв'ю та оцінка людей - справа хитра. Є кілька аспектів кандидата, які інтерв'юер, можливо, бажає дослідити, але одним із найважливіших є здатність ідивідуального до критичного мислення. З найрізноманітніших причин, не останньою з яких є те, що критичне мислення є погано визначеним, у нас дуже важко оцінювати навички критичного мислення.

Як інструктор з інженерії, один з моїх улюблених способів оцінювати здатність студента критично мислити - це поставити дещо неоднозначне запитання. Більш гострі студенти підхопили б несправне приміщення, відзначили це, і будь-яку відповідь, якщо врахувати передумову, або взагалі відмовитись від відповіді. Як правило, я б задавав питання, подібне до наступного:

Ви вибираєте креслення зі своєї роботи. Малюнок містить безліч різних описів, але найважливіші точки вказує на горизонтальну поверхню і говорить "Ідеально плоска". Поверхня довжиною 5 "шириною на 16", а частина виконана з алюмінію. Як ви будете обробляти деталь для створення цієї функції?

(До речі, ви були б вражені тим, як часто така погана специфікація з’являється на робочому місці.)

Я очікую, що студенти визнають, що створити ідеальну особливість неможливо, і що вони заявлять про це у своїй відповіді. Я, як правило, присуджую бонусний бал, якщо вони скажуть, що повернуться до дизайнера і попросять роз'яснення, перш ніж зробити участь. Якщо студент продовжує розповідати, як вони збираються досягти .001 планарності чи іншої складеної вартості, я присуджую нульові бали. Це допомагає мені зазначити моїм учням, що їм потрібно думати про більшу картину.

Нижня лінія

Якщо я опитую інженера (чи подібну професію), я шукаю когось, хто може критично мислити і ставити під сумнів те, що було поставлено перед ним. Я хочу, щоб хтось задав питання "Чи має це сенс?" .

Немає сенсу просити ідеально рівну частину, бо немає такої речі, як ідеальна. Немає сенсу просити функцію, яка ніколи не повертає повторюваного значення, оскільки така гарантія неможлива. У програмуванні ми часто чуємо фразу "сміття, сміття." Якщо вам передають сміття відповідно до вимог, ваша етична відповідальність зупиняється і задавати будь-яке питання допоможе вам виявити справжній намір. Якщо я беру інтерв'ю з кандидатом, і я даю їм незрозумілу вимогу, я очікую роз'яснень.


5

Гарантувати унікальність складно, оскільки комп'ютери не мають нескінченно великих змінних. Жодна реальна машина Тьюрінга не може.

Як я це бачу, тут є дві проблеми, і обидві мають налагоджені рішення.

  • Паралельність. Кілька машин можуть потребувати значення одночасно. На щастя, сучасні процесори мають вбудовану паралельність, а деякі мови надають зручні для розробників засоби, щоб скористатися цим.
  • Унікальність. Хоча неможливо гарантувати унікальність, ми можемо мати довільно великі змінні, які можуть містити значення настільки великі, що реальній системі було б дуже важко вичерпати всі унікальні значення

Ось моє рішення на Java:

public class Foo {
  private static BigInteger value = BigInteger.ZERO;
  private static final Lock lock = new ReentrantLock();

  public static BigInteger nextValue() {
    try {
      lock.lock();
      value = value.add(BigInteger.ONE);
      return value;
    }
    finally {
      lock.unlock();
    }
  }
}

BigInteger - це цілочисельний тип довільного розміру. Він може зростати, утримуючи величини, які є досить великими, навіть якщо не нескінченними. Блокування забезпечує одночасність, тому одне і те ж значення не може бути повернуто двічі двома одночасними запитами, що обслуговуються окремими потоками.


Я думаю, припущення про те, що код буде використовуватися лише менше п'ятисот років, є вагомим припущенням. Якщо ви просто повернете зростаючі значення в 64-бітовому сховищі, ви будете добре довгий час. За 1 дзвінок за нас, у 584555 років.
Mooing Duck

1
Принаймні, у Java це 2 ^ 63 значення (настільки наполовину, що довше). Ще довше, ніж людська раса, ймовірно, буде існувати з огляду на нашу схильність один до одного вбивати. Незважаючи на те, я взяв більш теоретичний підхід. Реально, 64 (або 63) бітів повинно бути достатньо.

1
@Snowman: ЩО?!? Ваше рішення дійсне лише 250K років?!?!? НАСТУПНИЙ КАНДИДАТ !!!!!! :-)
Боб Джарвіс - Відновіть Моніку

0

Я би відкрив функцію через порт на сервері; для виклику функції запитуючий апарат запитує з'єднання і йому надається одне, при цьому призначається ідентифікаційний код (послідовний номер для простоти). Кожного разу, коли повідомлення надсилається порту, що вимагає унікального значення, значення генерується шляхом об'єднання хеша MD5 поточної дати та часу з хешем MD5 ідентифікаційного коду.

Якщо вони хочуть більш пуленебезпечного рішення, вони повинні були б уточнити свої фактичні вимоги, а не бути розпливчастими щодо речей.


-1
string uniq(string machine_id) 
{
   static long u = long.MinValue;
   Interlocked.Increment(ref u);

   //Time stamp with millisecond precison
   string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff",
                                            CultureInfo.InvariantCulture);

   return machine_id + "-" + timestamp + "-" + u;
}

Описаним вище способом ми можемо переконатися, що значення повернення відрізняється, навіть якщо є перезапуск або навіть якщо вони викликаються одночасно з різних машин.


Програмісти це про концептуальних питаннях і відповідях , як очікується , пояснити речі. Викидання скидів коду замість пояснення - це як копіювання коду з IDE на дошку: це може здатися звичним і навіть іноді бути зрозумілим, але це виглядає дивно ... просто дивно. У дошці немає компілятора
gnat

Дякуємо, гнат, що вказав на це, подбає про те, щоб пояснити рішення наступного разу
techExplorer
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.