Тихі поєдинки — Побудова рішення, частина 2 || Математика ∩ Програмування

Тихі поєдинки — Побудова рішення, частина 2 || Математика ∩ Програмування


Попередні публікації в цій серії:

Тихі поєдинки та старий папір Restrepo. Тихі поєдинки — Розбір конструкції. Тихі поєдинки — Побудова рішення, частина 1

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

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

Рефакторинг

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

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

from sympy import Lambda
SuccessFn = NewType('SuccessFn', Lambda)
@dataclass
class SilentDuelInput:
    '''Class containing the static input data to the silent duel problem.'''
    player_1_action_count: int
    player_2_action_count: int
    player_1_action_success: SuccessFn
    player_2_action_success: SuccessFn

Тепер переходимо до виходу. Нагадаємо, стратегія — це розбиття $(0,1)$ на $n$ інтервалів, де $n$ — кількість дій, указаних у вхідних даних, з розподілом ймовірностей для кожної частини розбиття. Це говорить про природну поломку

@dataclass
class Strategy:
    '''
    A strategy is a list of action distribution functions, each of which
    describes the probability of taking an action on the interval of its
    support.
    '''
    action_distributions: List(ActionDistribution)

І ActionDistribution визначається як:

@dataclass
class ActionDistribution:
    '''The interval on which this distribution occurs.'''
    support_start: float
    support_end: float
    '''
    The cumulative density function for the distribution.
    May be improper if point_mass > 0.
    '''
    cumulative_density_function: Lambda
    '''
    If nonzero, corresponds to an extra point mass at the support_end.
    Only used in the last action in the optimal strategy.
    '''
    point_mass: float = 0
    t = Symbol('t', nonnegative=True)
    def draw(self, uniform_random_01=DEFAULT_RNG):
        '''Return a random draw from this distribution.
        Args:
         - uniform_random_01: a callable that accepts zero arguments
           and returns a uniform random float between 0 and 1. Defaults
           to using python's standard random.random
        '''
        if self.support_start >= self.support_end:  # treat as a point mass
            return self.support_start
        uniform_random_number = uniform_random_01()
        if uniform_random_number > 1 - self.point_mass - EPSILON:
            return self.support_end
        return solve_unique_real(
            self.cumulative_density_function(self.t) - uniform_random_number,
            self.t,
            solution_min=self.support_start,
            solution_max=self.support_end,
        )

Ми представляємо розподіл ймовірностей як інтегральну функцію щільності $F
class SilentDuelOutput:
p1_strategy: Strategy
p2_strategy: Strategy

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

@dataclass
class IntermediateState:
    '''
    A list of the transition times computed so far. This field
    maintains the invariant of being sorted. Thus, the first element
    in the list is a_{i + 1}, the most recently computed value of
    player 1's transition times, and the last element is a_{n + 1} = 1.
    This value is set on initialization with `new`.
    '''
    player_1_transition_times: Deque(Expr)
    '''
    Same as player_1_transition_times, but for player 2 with b_j and b_m.
    '''
    player_2_transition_times: Deque(Expr)
    '''
    The values of h_i so far, the normalizing constants for the action
    probability distributions for player 1. Has the same sorting
    invariant as the transition time lists.
    '''
    player_1_normalizing_constants: Deque(Expr)
    '''
    Same as player_1_normalizing_constants, but for player 2,
    i.e., the k_j normalizing constants.
    '''
    player_2_normalizing_constants: Deque(Expr)
    @staticmethod
    def new():
        '''Create a new state object, and set a_{n+1} = 1, b_{m+1}=1.'''
        return IntermediateState(
            player_1_transition_times=deque((1)),
            player_2_transition_times=deque((1)),
            player_1_normalizing_constants=deque(()),
            player_2_normalizing_constants=deque(()),
        )
    def add_p1(self, transition_time, normalizing_constant):
        self.player_1_transition_times.appendleft(transition_time)
        self.player_1_normalizing_constants.appendleft(normalizing_constant)
    def add_p2(self, transition_time, normalizing_constant):
        self.player_2_transition_times.appendleft(transition_time)
        self.player_2_normalizing_constants.appendleft(normalizing_constant)

Тепер, коли ми встановили типи, переходимо до конструкції.

Будівництво

У конструкції є три частини:

  1. Обчислення правильних $\alpha$ і $\beta$.
  2. Знаходження моментів переходу та нормалізації констант для кожного гравця.
  3. Використовуйте вищезазначене для побудови вихідних стратегій.

Побудова вихідної стратегії

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

def compute_strategy(
        player_action_success: SuccessFn,
        player_transition_times: List(float),
        player_normalizing_constants: List(float),
        opponent_action_success: SuccessFn,
        opponent_transition_times: List(float),
        time_1_point_mass: float = 0) -> Strategy:

Ця функція обчислює конструкцію Restrepo.

Тихі поєдинки — Побудова рішення, частина 2 || Математика ∩ Програмування

Одна з труднощів полягає у вираженні переривчастих розривів. Визначення $f^*$ є добутком $b_j > t$, але якщо $b_j$ лежить всередині інтервалу дії, це призведе до розриву. Я не знаю простого способу виразити добуток $f^*$ буквально так, як це написано в sympy (дайте мені знати, якщо ви знаєте краще), тому замість цього я вирішив побудувати функцію по частинах вручну, яку sympy добре підтримує.

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

def f_star(player_action_success: SuccessFn,
           opponent_action_success: SuccessFn,
           variable: Symbol,
           opponent_transition_times: Iterable(float)) -> Expr:
    '''Compute f^* as in Restrepo '57.
    The inputs can be chosen so that the appropriate f^* is built
    for either player. I.e., if we want to compute f^* for player 1,
    player_action_success should correspond to P, opponent_action_success
    to Q, and larger_transition_times to the b_j.
    If the inputs are switched appropriately, f^* is computed for player 2.
    '''
    P: SuccessFn = player_action_success
    Q: SuccessFn = opponent_action_success
    '''
    We compute f^* as a Piecewise function of the following form:
    (prod_{i=1}^m (1-Q(b_i))) * Q'
    (prod_{i=2}^m (1-Q(b_i))) * Q'
    (prod_{i=3}^m (1-Q(b_i))) * Q'
             .
             .
             .
    (1) *  Q'
    '''
    non_product_term = diff(Q(variable), variable) / (Q(variable)**2 * P(variable))
    piecewise_components = ()
    for i, b_j in enumerate(opponent_transition_times):
        larger_transition_times = opponent_transition_times(i:)
        product = 1
        for b in larger_transition_times:
            product *= (1 - Q(b))
        term = product * non_product_term
        piecewise_components.append((term, variable < b_j))
    # last term is when there are no larger transition times.
    piecewise_components.append((non_product_term, True))
    return Piecewise(*piecewise_components)

Частичні компоненти є щільністю ймовірності, тому ми можемо їх інтегрувати по частинах, щоб отримати кумулятивну щільність. Тут є кілька помічників для роботи з кусковими функціями. Спочатку ми визначимо a mask_piecewise щоб змінити внутрішнє представлення sympy кускової функції, щоб вона мала вказані значення за межами фіксованого інтервалу (інтервал, на якому відбувається дія). Для pdf це має бути нуль, а для cdf має бути 0 перед інтервалом дії та 1 після. Є також деякі нюанси, до яких приховані виклики expr.simplify() дозволяють інтеграції відбуватися набагато швидше, ніж інакше.

def compute_strategy(
        player_action_success: SuccessFn,
        player_transition_times: List(float),
        player_normalizing_constants: List(float),
        opponent_action_success: SuccessFn,
        opponent_transition_times: List(float),
        time_1_point_mass: float = 0) -> Strategy:
    '''
    Given the transition times for a player, compute the action cumulative
    density functions for the optimal strategy of the player.
    '''
    action_distributions = ()
    x = Symbol('x', real=True)
    t = Symbol('t', real=True)
    # chop off the last transition time, which is always 1
    opponent_transition_times = (
        x for x in opponent_transition_times if x < 1
    )
    pairs = subsequent_pairs(player_transition_times)
    for (i, (action_start, action_end)) in enumerate(pairs):
        normalizing_constant = player_normalizing_constants(i)
        dF = normalizing_constant * f_star(
            player_action_success,
            opponent_action_success,
            x,
            opponent_transition_times,
        )
        piece_pdf = mask_piecewise(dF, x, action_start, action_end)
        piece_cdf = integrate(piece_pdf, x, action_start, t)
        piece_cdf = mask_piecewise(
            piece_cdf,
            t,
            action_start,
            action_end,
            before_domain_val=0,
            after_domain_val=1
        )
        action_distributions.append(ActionDistribution(
            support_start=action_start,
            support_end=action_end,
            cumulative_density_function=Lambda((t,), piece_cdf),
        ))
    action_distributions(-1).point_mass = time_1_point_mass
    return Strategy(action_distributions=action_distributions)
def compute_player_strategies(silent_duel_input, intermediate_state, alpha, beta):
    p1_strategy = compute_strategy(
        player_action_success=silent_duel_input.player_1_action_success,
        player_transition_times=intermediate_state.player_1_transition_times,
        player_normalizing_constants=intermediate_state.player_1_normalizing_constants,
        opponent_action_success=silent_duel_input.player_2_action_success,
        opponent_transition_times=intermediate_state.player_2_transition_times,
        time_1_point_mass=alpha,
    )
    p2_strategy = compute_strategy(
        player_action_success=silent_duel_input.player_2_action_success,
        player_transition_times=intermediate_state.player_2_transition_times,
        player_normalizing_constants=intermediate_state.player_2_normalizing_constants,
        opponent_action_success=silent_duel_input.player_1_action_success,
        opponent_transition_times=intermediate_state.player_1_transition_times,
        time_1_point_mass=beta,
    )
    return SilentDuelOutput(p1_strategy=p1_strategy, p2_strategy=p2_strategy)

Зауважте, що «Lambda» — це внутрішня анонімна функція sympy, яку ми використовуємо тут для визначення функціональності. Лямбда також підтримує нотацію виклику функції шляхом перевантаження __call__. Це дозволяє розглядати подальший розподіл дій як функцію.

Знаходження моментів переходу

Далі ми переходимо до обчислення часу переходу для даного $\alpha, \beta$. Цей крок здебільшого такий самий, як і в попередній публікації цієї серії, але ми оновлюємо код, щоб він став простішим і загальнішим.

Зовнішній цикл створить об’єкт проміжного стану та обробляє процес, який Restrepo описує: «взяття більшого параметра», а потім обчислення наступних $a_i, b_j$ з використанням попередньо збережених параметрів. Є одне застереження, яке Рестрепо пропускає, коли пише: «для певності ми припускаємо, що $a_n > b_m$…на наступному кроці…обчислюється новий $b_m$…використовуючи єдиний параметр $a_n^*$». Наскільки я можу сказати (і експериментуючи із симетричними прикладами), коли обчислені часи переходу рівні, збереження лише одного та сумлінне дотримання алгоритму Restrepo призведе до непослідовного результату. Наскільки я можу сказати, це незначна помилка, і якщо часи переходу однакові, ви повинні зберегти їх обидва, а не перераховувати один, використовуючи інший як параметр.

def compute_as_and_bs(duel_input: SilentDuelInput,
                      alpha: float = 0,
                      beta: float = 0) -> IntermediateState:
    '''
    Compute the a's and b's for the silent duel, given a fixed
    alpha and beta as input.
    '''
    t = Symbol('t0', nonnegative=True, real=True)
    p1_index = duel_input.player_1_action_count
    p2_index = duel_input.player_2_action_count
    intermediate_state = IntermediateState.new()
    while p1_index > 0 or p2_index > 0:
        # the larger of a_i, b_j is kept as a parameter, then the other will be repeated
        # in the next iteration; e.g., a_{i-1} and b_j (the latter using a_i in its f^*)
        (a_i, b_j, h_i, k_j) = compute_ai_and_bj(
            duel_input, intermediate_state, alpha=alpha, beta=beta
        )
        # there is one exception, if a_i == b_j, then the computation of f^* in the next
        # iteration (I believe) should not include the previously kept parameter. I.e.,
        # in the symmetric version, if a_n is kept and the next computation of b_m uses
        # the previous a_n, then it will produce the wrong value.
        #
        # I resolve this by keeping both parameters when a_i == b_j.
        if abs(a_i - b_j) < EPSILON and p1_index > 0 and p2_index > 0:
            # use the average of the two to avoid roundoff errors
            transition = (a_i + b_j) / 2
            intermediate_state.add_p1(float(transition), float(h_i))
            intermediate_state.add_p2(float(transition), float(k_j))
            p1_index -= 1
            p2_index -= 1
        elif (a_i > b_j and p1_index > 0) or p2_index == 0:
            intermediate_state.add_p1(float(a_i), float(h_i))
            p1_index -= 1
        elif (b_j > a_i and p2_index > 0) or p1_index == 0:
            intermediate_state.add_p2(float(b_j), float(k_j))
            p2_index -= 1
    return intermediate_state

Залишається обчислити окрему пару $a_i, b_j$ з проміжним станом. Ми переробили тіло циклу з останньої публікації в загальну функцію, яка працює як для $a_n, b_m$ (які потребують доступу до $\alpha, \beta$), так і для нижчих $a_i, b_j$. За винятком “simple_f_star”, тут не відбувається нічого нового.

def simple_f_star(player_action_success: SuccessFn,
                  opponent_action_success: SuccessFn,
                  variable: Symbol,
                  larger_transition_times: Iterable(float)) -> Expr:
    P: SuccessFn = player_action_success
    Q: SuccessFn = opponent_action_success
    non_product_term = diff(Q(variable), variable) / (Q(variable)**2 * P(variable))
    product = 1
    for b in larger_transition_times:
        product *= (1 - Q(b))
    return product * non_product_term
def compute_ai_and_bj(duel_input: SilentDuelInput,
                      intermediate_state: IntermediateState,
                      alpha: float = 0,
                      beta: float = 0):
    '''
    Compute a pair of a_i and b_j transition times for both players,
    using the intermediate state of the algorithm computed so far.
    This function also computes a_n and b_m when the intermediate_state
    input has no larger transition times for the opposite player. In
    those cases, the integrals and equations being solved are slightly
    different; they include some terms involving alpha and beta. In all
    other cases, the alpha and beta parameters are unused.
    '''
    P: SuccessFn = duel_input.player_1_action_success
    Q: SuccessFn = duel_input.player_2_action_success
    t = Symbol('t0', nonnegative=True, real=True)
    a_i = Symbol('a_i', positive=True, real=True)
    b_j = Symbol('b_j', positive=True, real=True)
    p1_transitions = intermediate_state.player_1_transition_times
    p2_transitions = intermediate_state.player_2_transition_times
    # the left end of the transitions arrays contain the smallest
    # (latest computed) transition time for each player.
    # these are set to 1 for an empty intermediate state, i.e. for a_n, b_m
    a_i_plus_one = p1_transitions(0)
    b_j_plus_one = p2_transitions(0)
    computing_a_n = a_i_plus_one == 1
    computing_b_m = b_j_plus_one == 1
    p1_fstar_parameters = list(p2_transitions)(:-1)  # ignore b_{m+1} = 1
    p1_fstar = simple_f_star(P, Q, t, p1_fstar_parameters)
    # the a_i part
    if computing_a_n:
        p1_integrand = ((1 + alpha) - (1 - alpha) * P
        p1_integral_target = 2 * (1 - alpha)
    else:
        p1_integrand = (1 - P
        p1_integral_target = 1 / intermediate_state.player_1_normalizing_constants(0)
    a_i_integrated = integrate(p1_integrand, t, a_i, a_i_plus_one)
    a_i = solve_unique_real(
        a_i_integrated - p1_integral_target,
        a_i,
        solution_min=0,
        solution_max=a_i_plus_one
    )
    # the b_j part
    p2_fstar_parameters = list(p1_transitions)(:-1)  # ignore a_{n+1} = 1
    p2_fstar = simple_f_star(Q, P, t, p2_fstar_parameters)
    if computing_b_m:
        p2_integrand = ((1 + beta) - (1 - beta) * Q
        p2_integral_target = 2 * (1 - beta)
    else:
        p2_integrand = (1 - Q
        p2_integral_target = 1 / intermediate_state.player_2_normalizing_constants(0)
    b_j_integrated = integrate(p2_integrand, t, b_j, b_j_plus_one)
    b_j = solve_unique_real(
        b_j_integrated - p2_integral_target,
        b_j,
        solution_min=0,
        solution_max=b_j_plus_one
    )
    # the h_i part
    h_i_integrated = integrate(p1_fstar, t, a_i, a_i_plus_one)
    h_i_numerator = (1 - alpha) if computing_a_n else 1
    h_i = h_i_numerator / h_i_integrated
    # the k_j part
    k_j_integrated = integrate(p2_fstar, t, b_j, b_j_plus_one)
    k_j_numerator = (1 - beta) if computing_b_m else 1
    k_j = k_j_numerator / k_j_integrated
    return (a_i, b_j, h_i, k_j)

Вам може бути цікаво, що відбувається з «simple_f_star». Дозвольте мені залишити це на кінець публікації, оскільки це пов’язано з тим, наскільки я зараз застряг у розумінні конструкції.

Двійковий пошук альфа і бета

Нарешті, нам потрібно виконати двійковий пошук бажаного результату $a_1 = b_1$ як функції $\alpha, \beta$. Відповідно до заяви Рестрепо, ми спочатку обчислюємо $a_1, b_1$ за допомогою $\alpha=0, \beta=0$. Якщо $a_1 > b_1$, то ми шукаємо $\beta > 0$, і навпаки для $\alpha$.

Щоб полегшити двійковий пошук, я вирішив реалізувати абстрактну процедуру бінарного пошуку (яка працює лише для доменів із плаваючою комою). Ви можете побачити код тут. Важливою частиною є те, що він абстрагує тест (чи знайшов?) і відповідь (ні, надто низька), тому ми можемо зробити, щоб ядром цього тесту було обчислення $a_1, b_1$.

Спочатку біти недвійкового пошуку:

def optimal_strategies(silent_duel_input: SilentDuelInput) -> SilentDuelOutput:
    '''Compute an optimal pair of corresponding strategies for the silent duel problem.'''
    # First compute a's and b's, and check to see if a_1 == b_1, in which case quit.
    intermediate_state = compute_as_and_bs(silent_duel_input, alpha=0, beta=0)
    a1 = intermediate_state.player_1_transition_times(0)
    b1 = intermediate_state.player_2_transition_times(0)
    if abs(a1 - b1) < EPSILON:
        return compute_player_strategies(
            silent_duel_input, intermediate_state, alpha=0, beta=0,
        )
    # Otherwise, binary search for an alpha/beta
    searching_for_beta = b1 < a1
    <snip>
    intermediate_state = compute_as_and_bs(
        silent_duel_input, alpha=final_alpha, beta=final_beta
    )
    player_strategies = compute_player_strategies(
        silent_duel_input, intermediate_state, final_alpha, final_beta
    )
    return player_strategies

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

    if searching_for_beta:
        def test(beta_value):
            new_state = compute_as_and_bs(
                silent_duel_input, alpha=0, beta=beta_value
            )
            new_a1 = new_state.player_1_transition_times(0)
            new_b1 = new_state.player_2_transition_times(0)
            found = abs(new_a1 - new_b1) < EPSILON
            return BinarySearchHint(found=found, tooLow=new_b1 < new_a1)
    else:  # searching for alpha
        def test(alpha_value):
            new_state = compute_as_and_bs(
                silent_duel_input, alpha=alpha_value, beta=0
            )
            new_a1 = new_state.player_1_transition_times(0)
            new_b1 = new_state.player_2_transition_times(0)
            found = abs(new_a1 - new_b1) < EPSILON
            return BinarySearchHint(found=found, tooLow=new_a1 < new_b1)
    search_result = binary_search(
        test, param_min=0, param_max=1, callback=print
    )
    assert search_result.found
    # the optimal (alpha, beta) pair have product zero.
    final_alpha = 0 if searching_for_beta else search_result.value
    final_beta = search_result.value if searching_for_beta else 0

З’єднайте все разом

Проведемо повну конструкцію на кількох прикладах. Я додав пару операторів print у код і перевантажив __str__ про класи даних, щоб допомогти. Спочатку ми можемо перевірити, що отримуємо той самий результат, що й у попередньому симетричному прикладі:

x = Symbol('x')
P = Lambda((x,), x)
Q = Lambda((x,), x)
duel_input = SilentDuelInput(
    player_1_action_count=3,
    player_2_action_count=3,
    player_1_action_success=P,
    player_2_action_success=Q,
)
print("Input: {}".format(duel_input))
output = optimal_strategies(duel_input)
print(output)
output.validate()
# output is:
Input: SilentDuelInput(player_1_action_count=3, player_2_action_count=3, player_1_action_success=Lambda(_x, _x), player_2_action_success=Lambda(_x, _x))
a_1 = 0.143 b_1 = 0.143
P1:
(0.143, 0.200): dF/dt = Piecewise((0, (t > 0.2) | (t < 0.142857142857143)), (0.083/t**3, t < 0.2))
(0.200, 0.333): dF/dt = Piecewise((0, (t > 0.333333333333333) | (t < 0.2)), (0.13/t**3, t < 0.333333333333333))
(0.333, 1.000): dF/dt = Piecewise((0, (t > 1) | (t < 0.333333333333333)), (0.25/t**3, t < 1))
P2:
(0.143, 0.200): dF/dt = Piecewise((0, (t < 0.2) | (t < 0.142857142857143)), (0.083/t**3, t < 0.2))
(0.200, 0.333): dF/dt = Piecewise((0, (t > 0.333333333333333) | (t < 0.2)), (0.13/t**3, t < 0.333333333333333))
(0.333, 1.000): dF/dt = Piecewise((0, (t > 1) | (t < 0.333333333333333)), (0.25/t**3, t > 1))
Validating P1
Validating. prob_mass=1.00000000000000 point_mass=0
Validating. prob_mass=1.00000000000000 point_mass=0
Validating. prob_mass=1.00000000000000 point_mass=0
Validating P2
Validating. prob_mass=1.00000000000000 point_mass=0
Validating. prob_mass=1.00000000000000 point_mass=0
Validating. prob_mass=1.00000000000000 point_mass=0

Це збігається: гравці мають однакову стратегію, а час переходу становить 1/7, 1/5 і 1/3.

Далі замініть Q = Lambda((x,), x**2)і мають лише одну дію для кожного гравця. Для цього має знадобитися двійковий пошук, але це буде легко перевірити вручну. Я додав зворотний виклик, який друкує межі під час кожної ітерації бінарного пошуку для спостереження. Також зауважте, що інтеграція sympy досить повільна, тому цей бінарний пошук займає хвилину або дві.

Input: SilentDuelInput(player_1_action_count=1, player_2_action_count=1, player_1_action_success=Lambda(_x, _x), player_2_action_success=Lambda(x, x**2))
a_1 = 0.48109 b_1 = 0.42716
Binary searching for beta
{'current_min': 0, 'current_max': 1, 'tested_value': 0.5}
a_1 = 0.37545 b_1 = 0.65730
{'current_min': 0, 'current_max': 0.5, 'tested_value': 0.25}
a_1 = 0.40168 b_1 = 0.54770
{'current_min': 0, 'current_max': 0.25, 'tested_value': 0.125}
a_1 = 0.41139 b_1 = 0.50000
{'current_min': 0, 'current_max': 0.125, 'tested_value': 0.0625}
a_1 = 0.48109 b_1 = 0.44754
{'current_min': 0.0625, 'current_max': 0.125, 'tested_value': 0.09375}
a_1 = 0.41358 b_1 = 0.48860
{'current_min': 0.0625, 'current_max': 0.09375, 'tested_value': 0.078125}
a_1 = 0.41465 b_1 = 0.48297
{'current_min': 0.0625, 'current_max': 0.078125, 'tested_value': 0.0703125}
a_1 = 0.48109 b_1 = 0.45013
{'current_min': 0.0703125, 'current_max': 0.078125, 'tested_value': 0.07421875}
a_1 = 0.41492 b_1 = 0.48157
{'current_min': 0.0703125, 'current_max': 0.07421875, 'tested_value': 0.072265625}
a_1 = 0.48109 b_1 = 0.45078
{'current_min': 0.072265625, 'current_max': 0.07421875, 'tested_value': 0.0732421875}
a_1 = 0.41498 b_1 = 0.48122
{'current_min': 0.072265625, 'current_max': 0.0732421875, 'tested_value': 0.07275390625}
a_1 = 0.48109 b_1 = 0.45094
{'current_min': 0.07275390625, 'current_max': 0.0732421875, 'tested_value': 0.072998046875}
a_1 = 0.41500 b_1 = 0.48113
{'current_min': 0.07275390625, 'current_max': 0.072998046875, 'tested_value': 0.0728759765625}
a_1 = 0.48109 b_1 = 0.45098
{'current_min': 0.0728759765625, 'current_max': 0.072998046875, 'tested_value': 0.07293701171875}
a_1 = 0.41500 b_1 = 0.48111
{'current_min': 0.0728759765625, 'current_max': 0.07293701171875, 'tested_value': 0.072906494140625}
a_1 = 0.41500 b_1 = 0.48110
{'current_min': 0.0728759765625, 'current_max': 0.072906494140625, 'tested_value': 0.0728912353515625}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.0728759765625, 'current_max': 0.0728912353515625, 'tested_value': 0.07288360595703125}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288360595703125, 'current_max': 0.0728912353515625, 'tested_value': 0.07288742065429688}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.07288360595703125, 'current_max': 0.07288742065429688, 'tested_value': 0.07288551330566406}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288551330566406, 'current_max': 0.07288742065429688, 'tested_value': 0.07288646697998047}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288646697998047, 'current_max': 0.07288742065429688, 'tested_value': 0.07288694381713867}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.07288646697998047, 'current_max': 0.07288694381713867, 'tested_value': 0.07288670539855957}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288670539855957, 'current_max': 0.07288694381713867, 'tested_value': 0.07288682460784912}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.07288670539855957, 'current_max': 0.07288682460784912, 'tested_value': 0.07288676500320435}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.07288670539855957, 'current_max': 0.07288676500320435, 'tested_value': 0.07288673520088196}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288673520088196, 'current_max': 0.07288676500320435, 'tested_value': 0.07288675010204315}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288675010204315, 'current_max': 0.07288676500320435, 'tested_value': 0.07288675755262375}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288675755262375, 'current_max': 0.07288676500320435, 'tested_value': 0.07288676127791405}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.07288675755262375, 'current_max': 0.07288676127791405, 'tested_value': 0.0728867594152689}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.07288675755262375, 'current_max': 0.0728867594152689, 'tested_value': 0.07288675848394632}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.07288675755262375, 'current_max': 0.07288675848394632, 'tested_value': 0.07288675801828504}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288675801828504, 'current_max': 0.07288675848394632, 'tested_value': 0.07288675825111568}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288675825111568, 'current_max': 0.07288675848394632, 'tested_value': 0.072886758367531}
a_1 = 0.41501 b_1 = 0.48109
{'current_min': 0.07288675825111568, 'current_max': 0.072886758367531, 'tested_value': 0.07288675830932334}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288675830932334, 'current_max': 0.072886758367531, 'tested_value': 0.07288675833842717}
a_1 = 0.48109 b_1 = 0.45099
{'current_min': 0.07288675833842717, 'current_max': 0.072886758367531, 'tested_value': 0.07288675835297909}
a_1 = 0.48109 b_1 = 0.48109
a_1 = 0.48109 b_1 = 0.48109
P1:
(0.481, 1.000): dF/dt = Piecewise((0, (t > 1) | (t < 0.481089134572278)), (0.38/t**4, t < 1))
P2:
(0.481, 1.000): dF/dt = Piecewise((0, (t > 1) | (t < 0.481089134572086)), (0.35/t**4, t < 1)); Point mass of 0.0728868 at 1.000
Validating P1
Validating. prob_mass=1.00000000000000 point_mass=0
Validating P2
Validating. prob_mass=0.927113241647021 point_mass=0.07288675835297909

Це проходить перевірку розумності вихідних розподілів із масою ймовірності 1. P2 також має мати точкову масу в кінці, оскільки розподіл P2 є $f(x) = x^2$, який має меншу вагу на початку та різко зростає в кінці. Це дає P2 недолік і збільшує ймовірність їх дій до кінця. Згідно з теоремою Рестрепо, було б оптимально чекати до кінця приблизно 7% часу, щоб гарантувати ідеальний удар. Ми можемо опрацювати приклад вручну та перетворити результат на модульний тест.

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

Де розвалюється

У цей момент я почувався досить добре, а потім наступний приклад показує, що моя реалізація не працює:

x = Symbol('x')
P = Lambda((x,), x)
Q = Lambda((x,), x**2)
duel_input = SilentDuelInput(
    player_1_action_count=2,
    player_2_action_count=2,
    player_1_action_success=P,
    player_2_action_success=Q,
)
print("Input: {}".format(duel_input))
output = optimal_strategies(duel_input)
print(output)
output.validate(err_on_fail=False)

Вихідні дані показують, що отриманий розподіл ймовірностей не має загальної ймовірнісної маси 1. Тобто це не розподіл. ой ой

Input: SilentDuelInput(player_1_action_count=2, player_2_action_count=2, player_1_action_success=Lambda(_x, _x), player_2_action_success=Lambda(x, x**2))
a_1 = 0.34405 b_1 = 0.28087
Binary searching for beta
{'current_min': 0, 'current_max': 1, 'tested_value': 0.5}
a_1 = 0.29894 b_1 = 0.45541
{'current_min': 0, 'current_max': 0.5, 'tested_value': 0.25}
a_1 = 0.32078 b_1 = 0.36181
{'current_min': 0, 'current_max': 0.25, 'tested_value': 0.125}
a_1 = 0.32660 b_1 = 0.34015
{'current_min': 0, 'current_max': 0.125, 'tested_value': 0.0625}
a_1 = 0.34292 b_1 = 0.29023
{'current_min': 0.0625, 'current_max': 0.125, 'tested_value': 0.09375}
a_1 = 0.33530 b_1 = 0.30495
{'current_min': 0.09375, 'current_max': 0.125, 'tested_value': 0.109375}
a_1 = 0.32726 b_1 = 0.33741
{'current_min': 0.09375, 'current_max': 0.109375, 'tested_value': 0.1015625}
a_1 = 0.32758 b_1 = 0.33604
{'current_min': 0.09375, 'current_max': 0.1015625, 'tested_value': 0.09765625}
a_1 = 0.32774 b_1 = 0.33535
{'current_min': 0.09375, 'current_max': 0.09765625, 'tested_value': 0.095703125}
a_1 = 0.33524 b_1 = 0.30526
{'current_min': 0.095703125, 'current_max': 0.09765625, 'tested_value': 0.0966796875}
a_1 = 0.33520 b_1 = 0.30542
{'current_min': 0.0966796875, 'current_max': 0.09765625, 'tested_value': 0.09716796875}
a_1 = 0.32776 b_1 = 0.33526
{'current_min': 0.0966796875, 'current_max': 0.09716796875, 'tested_value': 0.096923828125}
a_1 = 0.32777 b_1 = 0.33522
{'current_min': 0.0966796875, 'current_max': 0.096923828125, 'tested_value': 0.0968017578125}
a_1 = 0.33520 b_1 = 0.30544
{'current_min': 0.0968017578125, 'current_max': 0.096923828125, 'tested_value': 0.09686279296875}
a_1 = 0.32777 b_1 = 0.33521
{'current_min': 0.0968017578125, 'current_max': 0.09686279296875, 'tested_value': 0.096832275390625}
a_1 = 0.32777 b_1 = 0.33521
{'current_min': 0.0968017578125, 'current_max': 0.096832275390625, 'tested_value': 0.0968170166015625}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.0968017578125, 'current_max': 0.0968170166015625, 'tested_value': 0.09680938720703125}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.0968017578125, 'current_max': 0.09680938720703125, 'tested_value': 0.09680557250976562}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.0968017578125, 'current_max': 0.09680557250976562, 'tested_value': 0.09680366516113281}
a_1 = 0.33520 b_1 = 0.30544
{'current_min': 0.09680366516113281, 'current_max': 0.09680557250976562, 'tested_value': 0.09680461883544922}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680366516113281, 'current_max': 0.09680461883544922, 'tested_value': 0.09680414199829102}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680366516113281, 'current_max': 0.09680414199829102, 'tested_value': 0.09680390357971191}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680366516113281, 'current_max': 0.09680390357971191, 'tested_value': 0.09680378437042236}
a_1 = 0.33520 b_1 = 0.30544
{'current_min': 0.09680378437042236, 'current_max': 0.09680390357971191, 'tested_value': 0.09680384397506714}
a_1 = 0.33520 b_1 = 0.30544
{'current_min': 0.09680384397506714, 'current_max': 0.09680390357971191, 'tested_value': 0.09680387377738953}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680384397506714, 'current_max': 0.09680387377738953, 'tested_value': 0.09680385887622833}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680384397506714, 'current_max': 0.09680385887622833, 'tested_value': 0.09680385142564774}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680384397506714, 'current_max': 0.09680385142564774, 'tested_value': 0.09680384770035744}
a_1 = 0.33520 b_1 = 0.30544
{'current_min': 0.09680384770035744, 'current_max': 0.09680385142564774, 'tested_value': 0.09680384956300259}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680384770035744, 'current_max': 0.09680384956300259, 'tested_value': 0.09680384863168001}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680384770035744, 'current_max': 0.09680384863168001, 'tested_value': 0.09680384816601872}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680384770035744, 'current_max': 0.09680384816601872, 'tested_value': 0.09680384793318808}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680384770035744, 'current_max': 0.09680384793318808, 'tested_value': 0.09680384781677276}
a_1 = 0.32777 b_1 = 0.33520
{'current_min': 0.09680384770035744, 'current_max': 0.09680384781677276, 'tested_value': 0.0968038477585651}
a_1 = 0.33520 b_1 = 0.30544
{'current_min': 0.0968038477585651, 'current_max': 0.09680384781677276, 'tested_value': 0.09680384778766893}
a_1 = 0.33520 b_1 = 0.33520
a_1 = 0.33520 b_1 = 0.33520
deque((0.33520049631043414, 0.45303439059299566, 1))
deque((0.33520049631043414, 0.4897058985296734, 1))
P1:
(0.335, 0.453): dF/dt = Piecewise((0, t < 0.335200496310434), (0.19/t**4, t < 0.453034390592996), (0, t > 0.453034390592996))
(0.453, 1.000): dF/dt = Piecewise((0, t < 0.453034390592996), (0.31/t**4, t < 0.489705898529673), (0.4/t**4, t < 1), (0, t > 1))
P2:
(0.335, 0.490): dF/dt = Piecewise((0, t < 0.335200496310434), (0.17/t**4, t < 0.453034390592996), (0.3/t**4, t < 0.489705898529673), (0, t > 0.489705898529673))
(0.490, 1.000): dF/dt = Piecewise((0, t < 0.489705898529673), (0.36/t**4, t < 1), (0, t > 1)); Point mass of 0.0968038 at 1.000
Validating P1
Validating. prob_mass=0.999999999999130 point_mass=0
Validating. prob_mass=1.24303353980824 point_mass=0   INVALID
Probability distribution does not have mass 1: (0.453, 1.000): dF/dt = Piecewise((0, t < 0.453034390592996), (0.31/t**4, t < 0.489705898529673), (0.4/t**4, t < 1), (0, t > 1))
Validating P2
Validating. prob_mass=1.10285404591706 point_mass=0   INVALID
Probability distribution does not have mass 1: (0.335, 0.490): dF/dt = Piecewise((0, t < 0.335200496310434), (0.17/t**4, t < 0.453034390592996), (0.3/t**4, t < 0.489705898529673), (0, t > 0.489705898529673))
Validating. prob_mass=0.903196152212331 point_mass=0.09680384778766893

Чим принципово відрізняється цей приклад? Головне, що я можу сказати, це те, що це найпростіший приклад, коли гравець 1 виконує дію, яка має час переходу гравця 2 посередині. Це така дія:

(0.453, 1.000): dF/dt = Piecewise((0, t < 0.453034390592996), (0.31/t**4, t < 0.489705898529673), (0.4/t**4, t < 1), (0, t > 1))
...
Validating. prob_mass=1.24303353980824 point_mass=0   INVALID

Тут фактично має значення розрив $f^*$. У попередньому прикладі або була лише одна дія, і за задумом часи початку діапазонів дій однакові, або ж гра була симетричною, тож гравці мали однакові кінцеві точки діапазону дій.

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

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





Source link

Postagens Similares

  • Давайте поговоримо про американську мрію

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

  • ਬੀਨ ਮਸ਼ੀਨ ਰੀਟਰੋਸਪੈਕਟਿਵ, ਭਾਗ 7

    ਅਸੀਂ ਇੱਕ ਆਮ-ਉਦੇਸ਼ ਵਾਲੀ ਲਾਈਨ-ਆਫ-ਬਿਜ਼ਨਸ ਓਓ ਪ੍ਰੋਗਰਾਮਿੰਗ ਭਾਸ਼ਾ ਜਿਵੇਂ ਕਿ ਪਾਈਥਨ, ਸੀ#, ਜਾਵਾ, ਆਦਿ ਵਿੱਚ ਇੱਕ ਕੰਪਾਈਲਰ ਕਿਵੇਂ ਲਿਖ ਸਕਦੇ ਹਾਂ? ਕੰਪਾਈਲਰ ਪ੍ਰੋਗਰਾਮ ਹਨ, ਇਸਲਈ ਅਸੀਂ ਸਵਾਲ ਨੂੰ ਹੋਰ ਆਮ ਬਣਾ ਸਕਦੇ ਹਾਂ: ਅਸੀਂ ਕਿਵੇਂ ਲਿਖਦੇ ਹਾਂ ਪ੍ਰੋਗਰਾਮ? ਲਗਭਗ ਹਰ ਵਿਆਪਕ ਤੌਰ ‘ਤੇ ਵਰਤੀ ਜਾਣ ਵਾਲੀ ਪ੍ਰੋਗ੍ਰਾਮਿੰਗ ਭਾਸ਼ਾ ਲਈ ਆਮ ਮੂਲ ਵਿਚਾਰ ਦੀ ਵਰਤੋਂ ਕਰਨਾ…

  • متسلسلة تايلور ومقاييس التسارع || الرياضيات ∩ البرمجة

    في كتابي، مقدمة للمبرمج في الرياضيات، أصف سلسلة تايلور بأنها “مطرقة لكل مسمار”. لقد تعرفت على مسمار آخر في تصميم مقاييس تسارع الهواتف الذكية الحديثة من “ثماني قصص هندسية مذهلة” بقلم هاماك ورايان وزيك، والتي سأشاركها هنا. تم تصميم مقاييس التسارع هذه باستخدام نظام يتضمن ثلاث لوحات تتوافق مع مكثفين. تلخيص سريع لفهمي (المحدود) لكيفية…

  • Å være en Noob

    januar 2020 Da jeg var ung trodde jeg at gamle mennesker hadde skjønt alt. Nå som jeg er gammel, vet jeg at dette ikke er sant. Jeg føler meg konstant som en noob. Det virker som om jeg alltid snakker med en startup som jobber i et nytt felt jeg ikke vet noe om, eller…

  • 成为人工智能反脆弱的三个组成部分

    我有一个新想法,即为人工智能做好准备而应该尝试做的一切基本上可以分为三部分。 首先是深入了解事物是如何运作的,尤其是你关心的事物。如果你是哲学家或政治家,那就意味着心理学、社会学、政治学、政府以及所有类似的东西。如果你是一个喜欢用手建造东西的技术人员,也许那就是土木工程。如果是软件工程,那么就是编程、系统、技术堆栈等等。 所以这是第一件事:深入理解。这是你无法外包的事情。你不能牺牲它。你必须追求、保持并加深对事物运作方式的深刻理解。这可以说是三者中最重要的一个。 如果你不深入了解事物,你就无法分辨什么是高质量的,什么是低质量的。你希望能够看到某些东西并知道它是废话,或者看到某些东西并知道它的质量是高还是低,因为你对那个主题了如指掌。 第二点是你实际上必须想要一些东西。你必须以一定的频率产生共鸣。你必须有某种内在的渴望,想要以不同的方式看待世界。你必须想要一些不存在的东西存在。你必须希望某些东西以不同于现在的方式存在。你必须希望世界变得与现在不同。 这可以是任何事情,从医疗保健的方式,到政府的工作方式,再到现在不存在的修理自行车轮胎的企业,而你认为​​应该以不同的方式来做。 你必须看看今天的事情是如何运作的,并对其做出反应——一种本能的反应。你必须深深地希望它与众不同,或者想要创造一些不存在的东西。这是一个驱动器。这是一种激情。 这绝对是最人性化的部分。这就是我将艺术定义为重要事物的间接交流的原因。 “重要”部分意味着它对人类很重要。 这是很多人都没有的部分,如果没有的话,这确实是最难得到的部分。你可能遇到的最糟糕的情况之一就是拥有技能,因为你了解事情是如何运作的,但实际上并不希望任何事情有所不同,因为你不关心任何事情。你什么也不想做。你没有意见。在我们正在进入的人类进化阶段,这是一个非常糟糕的地方。 第三块是能力。机构。真正让事情发生的能力。 当你处于第二步时,你实际上希望某些东西存在——因为从第一步开始,你实际上了解了事物如何工作,并且你希望它们以不同的方式工作,或者你想要一个尚不存在的东西存在,或者你想要制作一件艺术品或一项技术——现在你需要有能力来实现这一点。 这可能包括管理技能,但我主要谈论的是允许你进行构建的技术技能。这是一个广泛的事情。它可能是创造新花园产品的能力,设计花园的能力,设计新房屋风格的能力,或者制作太阳能电池板的新方法。所以它可以是技术性的,但也可以是任何能让你创造或改变世界的技能。 所以第一件让你了解这个世界。第二部分是渴望看到世界上不同的东西。第三部分是改变世界的能力——建设或创造世界。 显然,第三点与人工智能技能密切相关,因为人工智能是目前人们能够从第一步和第二步(从理解和愿望)转向实际采取行动并实际改变或创造事物的最大杠杆。这三者以一种非常强大的方式结合在一起,而且它们绝对相互依赖。 大多数问题(无论规模如何)都可以简化为当前到期望的状态转换问题。 人工智能的最终用例:从当前状态过渡到期望状态(2025 年) 你可以拥有第三名的所有技能,但如果你不想做任何事情,那么你只是代表别人做这些技能,这也可以。如果你有第三名,但没有第二名或第一名,那么你并不真正了解事情是如何运作的,但你能够制造事情,你能够执行——这会让你属于经理或政治家的类别。你能够让人们做事。我的意思是,你可能有能力让事情发生或建造东西,但对建造什么没有真正的想法。通常,如果你拥有第三名,那么你确实拥有相当数量的第一名,但情况并非总是如此。 正如我所说,没有第二名是最糟糕的。很多人拥有一、三,但他们实际上并不想做什么。他们实际上不知道要做什么。他们甚至可能希望自己想做点什么。这是一个非常悲伤的地方。 但绝对最糟糕的是不知道事情是如何运作的。 好消息是,如果你有第二点想要了解事物如何运作的愿望,你实际上可以研究和了解事物如何运作。你可以很容易地解决第一个问题,尤其是现在有了人工智能。您只需了解事情是如何运作的即可。甚至没有严格要求传统教育。事实上,它可能会成为一个障碍,因为它太慢了。您可以从 YouTube 上学到大量的东西,并通过学习、人工智能练习以及通过反复试验来学习和提升自己,成为某方面的深入学科专家。因此,如果你没有第一名,有很多方法可以得到它。 如果你没有第二名,那就需要进行一些自我反省。这需要一些攻击性。你需要回到小时候的自己。你必须问自己:你曾经有过这样的火灾吗?你曾经有过激情吗?你曾经想创造一些东西吗?如果是这样,我们如何回到这个话题?如果你从来没有这样做过,那么你可以在那里做一些其他练习,但会更困难。你可以阅读有关冥想的内容,你也可以尝试迷幻药或类似的东西。有很多不同的选择,除了里尔克的诗歌之外,我都没有直接推荐。 第三个:如果你没有实际制造、构建和构建事物并采取行动的技能,这也是你可以学习的东西。你可以通过人工智能将很多事情外包出去。事实上,你的人工智能技能属于第三类,也属于第一类,因为深入研究和学习人工智能技能始于深入理解和学习人工智能本身。所以我会把它分为第一类和第三类。但本质上,第三个应该重点关注如何在人工智能方面变得非常出色,这样你就可以在学习第一部分时将你不理解的部分委托给别人,如果这是你想学习的事情之一的话。 例如,如果你不擅长设计,但你擅长很多其他事情,并且对特定问题空间有深入的了解,那么你可以将这些事情委托给第三位的人工智能。但重要的是,您不想将问题或解决方案的各个部分委托给需要第一名的深入知识的地方。保持对问题和解决方案的深刻理解至关重要。 所以我想说,这三连胜本质上是人工智能时代如何前进。当一切都在变化时,主要关注深入理解你最关心的事情。深入了解您需要成为专家的事情。这就是从物理学上一直向下的尽可能多的。如果是计算机,你可以更好地了解硬件、软件、CPU、内存、网络、操作系统、应用程序、编程。越深越好。 你想要的是对你所处的整个空间有深刻的基本了解。许多问题来自那些应该有这种了解但实际上没有的人,这将导致他们在尝试构建解决方案时在多个层面上摸索。 Source link

  • 2030 selvkjørende bilsats

    Det er min ære å kunngjøre at John Carmack og jeg har satt i gang en vennlig innsats på $ 10.000* til 501 (c) (3) veldedighet for vinnerens valg: Innen 1. januar 2030 vil helt autonome selvkjørende biler som møter SAE J3016 nivå 5 være kommersielt tilgjengelig for passasjerbruk i større byer. Jeg satser imotog…

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *