Честный разбор с решением, которое не стыдно показать на собеседовании, я всё же сделаю :)
Идея 💡
Задача состоит в том, чтобы разделить массив на максимальное количество блоков, так чтобы сортировка каждого блока и их объединение давали отсортированный массив. Ключевая идея заключается в том, что элементы из одного цикла перестановки
arr
1 → arr
2 → arr
3 → ... → arr
n → arr
1
обязательно должны находиться в одном блоке, так как они взаимозависимы.
Описание подхода 🛠️
- Создаём массив
chunk_assignment
, чтобы отслеживать принадлежность каждого индекса к определённому блоку.
- Используем функцию
assign_chunk
для рекурсивного назначения всех индексов, связанных с текущим индексом, в один блок. Она:
- Обновляет максимальный индекс текущего блока.
- Помечает все посещённые индексы текущим номером блока.
- Внешний цикл:
- Перебирает индексы массива.
- Увеличивает счётчик блоков при обнаружении нового блока.
- Завершает обработку текущего блока, когда все его элементы покрыты.
- Возвращаем общее количество блоков
Асимптотика 📊
- Время:
O(n)
, так как каждый элемент обрабатывается один раз.
- Память:
O(n)
, для хранения массива chunk_assignment
.
Исходный код решения 📜
impl Solution {
pub fn max_chunks_to_sorted(arr: Vec<i32>) -> i32 {
let n = arr.len();
let mut chunk_assignment = vec![0; n]; // Tracks which chunk each index belongs to
let mut chunk_count = 0; // Counter for the number of chunks
// Helper function to assign indices to a chunk
let mut assign_chunk = |start_idx: usize, chunk: i32, max_idx: &mut usize| {
let mut current_idx = start_idx;
while chunk_assignment[current_idx] == 0 {
*max_idx = (*max_idx).max(current_idx);
chunk_assignment[current_idx] = chunk;
current_idx = arr[current_idx] as usize;
}
};
let mut start_idx = 0;
while start_idx < n {
chunk_count += 1; // Start a new chunk
let mut max_idx = start_idx;
// Process the current chunk until all related indices are covered
loop {
assign_chunk(start_idx, chunk_count, &mut max_idx);
if start_idx == max_idx {
break; // The current chunk is complete
}
start_idx += 1; // Move to the next index
}
start_idx += 1; // Move to the next unprocessed index
}
chunk_count // Return the total number of chunks
}
}
перейти
Небезынтересно выглядит реализация этого же подхода на Go
– у них весьма своеобразно реализуются итераторы в неожиданном для Go функциональном стиле. В реальной же практике такое редко гошники практикуют, как я понимаю, — гораздо проще байты строки in-place инвертировать.
// CharLockPair represents a character and its corresponding lock state.
type CharLockPair struct {
char, lock rune
}
// Seq defines a functional iterator for yielding elements of type T.
type Seq[T any] func(func(T) bool)
// canBeValid checks if the given string can be a valid parentheses string.
func canBeValid(s, locked string) bool {
if len(s)%2 != 0 {
return false
}
// Forward pass
if !checkBalance(forwardIter(s, locked)) {
return false
}
// Backward pass
if !checkBalance(backwardIter(s, locked)) {
return false
}
return true
}
// checkBalance processes the iterator and determines if the sequence is balanced.
func checkBalance(seq Seq[CharLockPair]) bool {
balance, free := 0, 0
for pair := range seq {
char, lock := pair.char, pair.lock
if lock == '0' {
free++
} else if char == '(' {
balance++
} else {
balance--
}
if balance < 0 {
if free == 0 {
return false // Stop iteration early
}
balance++
free--
}
}
return balance <= free
}
// forwardIter generates an iterator for the forward pass.
func forwardIter(s, locked string) Seq[CharLockPair] {
return func(yield func(CharLockPair) bool) {
for i := 0; i < len(s); i++ {
if !yield(CharLockPair{char: rune(s[i]), lock: rune(locked[i])}) {
return
}
}
}
}
// backwardIter generates an iterator for the backward pass with swapped characters.
func backwardIter(s, locked string) Seq[CharLockPair] {
return func(yield func(CharLockPair) bool) {
for i := len(s) - 1; i >= 0; i-- {
char := rune(s[i])
if char == '(' {
char = ')'
} else {
char = '('
}
if !yield(CharLockPair{char: char, lock: rune(locked[i])}) {
return
}
}
}
}
перейти
Полезно заметить, что если использовать для разбиения на компоненты связности подход с Disjoint Set Unions
, — можно получить решение, работающее на практике в 2 раза быстрее за счёт более эффективных аллокаций памяти (нет необходимости поддерживать списки смежности).
struct UnionFind {
parent: Vec<usize>,
size: Vec<usize>,
min_and: Vec<i32>,
}
impl Solution {
pub fn minimum_cost(n: i32, edges: Vec<Vec<i32>>, query: Vec<Vec<i32>>) -> Vec<i32> {
let n = n as usize;
let mut uf = UnionFind::new(n);
// Process edges to construct the Union-Find structure
for edge in &edges {
let (u, v, w) = (edge[0] as usize, edge[1] as usize, edge[2]);
uf.union(u, v, w);
}
// Process queries efficiently
query.into_iter().map(|q| {
let (s, t) = (uf.find(q[0] as usize), uf.find(q[1] as usize));
if s == t { uf.min_and[s] } else { -1 }
}).collect()
}
}
impl UnionFind {
fn new(n: usize) -> Self {
UnionFind {
parent: (0..n).collect(),
size: vec![1; n],
min_and: vec![-1; n],
}
}
fn find(&mut self, x: usize) -> usize {
if self.parent[x] != x {
self.parent[x] = self.find(self.parent[x]); // Path compression
}
self.parent[x]
}
fn union(&mut self, a: usize, b: usize, weight: i32) {
let mut root_a = self.find(a);
let mut root_b = self.find(b);
if root_a != root_b {
// Union by size: attach the smaller tree under the larger tree
if self.size[root_a] < self.size[root_b] {
std::mem::swap(&mut root_a, &mut root_b);
}
self.parent[root_b] = root_a;
self.size[root_a] += self.size[root_b];
self.min_and[root_a] &= self.min_and[root_b];
}
// Update min_and for the new root
self.min_and[root_a] &= weight;
}
}
перейти
Про Шекли ты уже сказал, надеюсь не имеешь в виду только один его рассказ.
Из менее известных очень рекомендую Грега Игана. Кроме рассказов, у него и романы суперские ("Карантин", "Город перестановок", остальные тоже неплохи, но мне уже меньше понравились).
Можно начать с рассказов "Во тьму" и "Тёмные целые", чтобы оценить оригинальность идей. А потом просто любой брать и наслаждаться.
перейти
Попробуй достать архив журнала "Полдень, XXI век", мне кажется, как раз то, что тебе может быть интересно. Он закрылся в 2013, сейчас под таким же названием выпускаются периодические альманахи, но про них я не могу сказать, ок или нет.
перейти
Интересным выглядит способ, как красиво декомпозировать решение.
Для этого мы выделим итератор по всем кортежам {(x,y) | x+y=z
} в отдельный метод.
use std::collections::HashMap;
use std::cmp::Ordering;
impl Solution {
pub fn len_longest_fib_subseq(arr: Vec<i32>) -> i32 {
let n = arr.len();
let mut dp: HashMap<(i32, i32), i32> = HashMap::new(); // DP table storing lengths of valid subsequences
// Iterate over each element z as a potential end of a Fibonacci-like sequence
for k in 2..n {
let z = arr[k];
// Use an iterator to find all valid pairs (x, y) where x + y = z
for (x, y) in Self::pair_sum_iterator(arr[..k].iter(), z) {
// Apply DP transition: dp[z, y] = dp[y, x] + 1, defaulting to 2 if not found
let cur_len = dp.get(&(y, x)).unwrap_or(&2) + 1;
// Store computed length for (z, y)
dp.insert((z, y), cur_len);
}
}
// Return the longest found subsequence length, or 0 if none exist
*dp.values().max().unwrap_or(&0)
}
/// A helper function that iterates over pairs (x, y) in a sorted array slice,
/// finding pairs where x + y == target_sum using a two-pointer approach.
fn pair_sum_iterator<'a>(mut iter: impl DoubleEndedIterator<Item = &'a i32>, target_sum: i32) -> impl Iterator<Item = (i32, i32)> {
std::iter::from_fn(move || {
let mut front = iter.next();
let mut back = iter.next_back();
while let (Some(&x), Some(&y)) = (front, back) {
match (x + y).cmp(&target_sum) {
Ordering::Equal => return Some((x, y)), // Found a valid pair (x, y)
Ordering::Less => front = iter.next(), // Increase x to increase the sum
Ordering::Greater => back = iter.next_back(), // Decrease y to reduce the sum
}
}
None
})
}
}
перейти
1) Тед Чан
2) Если читаешь по-английски, то рекомендую qntm, особенно вот эти:
- https://qntm.org/responsibilit
- https://qntm.org/mmacevedo (есть переводы на русский, в т.ч. мой)
- https://qntm.org/gorg
перейти
Ещё один простой вариант решения - собрать все слайсы, отвечающие словам в одну коллекцию и проджойнить стандартными средствами через пробел.
impl Solution {
pub fn add_spaces(s: String, spaces: Vec<i32>) -> String {
// Convert indices to usize
let mut spaces: Vec<usize> = spaces.into_iter().map(|s_pos| s_pos as usize).collect();
// Add the start (0) and end (s.len()) boundaries to spaces
spaces.insert(0, 0);
spaces.push(s.len());
// Create slices from spaces indices
let slices: Vec<&str> = spaces.windows(2)
.map(|window| &s[window[0]..window[1]])
.collect();
// Join the slices with spaces
slices.join(" ")
}
}
В этом коде есть один грустный момент - необходимость создания промежуточного вектора из слайсов исходной строки.
К сожалению, join
из стандартной библиотеки позволяет только такое использование.
Но, если мы не ограничеы стандартной библиотекой, то крейт itertools спешит нам на помощь ;)
перейти
На практических масштабах решение со сложностью O(N²logN)
(сложить все произведения в вектор, который потом отсортировать и посчитать частоты по нему) работает немного (на 20%) быстрее.
Хороший пример, когда константа от хэш-таблицы даёт больший вклад, чем логарифм от сортировки!
impl Solution {
pub fn tuple_same_product(nums: Vec<i32>) -> i32 {
// Build vector of products for all unique pairs.
let mut products: Vec<_> = (1..nums.len())
.flat_map(|j| (0..j).map(|i| nums[i] * nums[j]).collect::<Vec<_>>())
.collect();
// Sort to group identical products.
products.sort_unstable();
// Group products, calculate frequency-based contributions, and sum them.
4 * products.chunk_by(|a, b| a == b)
.filter_map(|group| if group.len() > 1 {Some(group.len() as i32)} else {None})
.map(|freq| freq * (freq - 1))
.sum::<i32>()
}
}
перейти
Можно предоставить также и решение за один проход.
Мне кажется, что оно менее изящно. Но всё-равно имеет право быть упомянутым:
use std::cmp::Ordering;
impl Solution {
pub fn longest_monotonic_subarray(nums: Vec<i32>) -> i32 {
nums.windows(2).fold((1, 1, 1), |(max_chain, inc_chain, dec_chain), w| {
match w[1].cmp(&w[0]) {
// extend decreasing chain; reset increasing chain; update max_chain
Ordering::Less => (max_chain.max(dec_chain + 1), 1, dec_chain + 1),
// reset both chains on equal elements
Ordering::Equal => (max_chain, 1, 1),
// extend increasing chain; reset decreasing chain; update max_chain
Ordering::Greater => (max_chain.max(inc_chain + 1), inc_chain + 1, 1),
}
}).0
}
}
перейти
Кстати, используя кучу 3 максимумов, можно упростить код и выкинуть отдельный метод get_triple_length(...)
.
Достаточно просто с каждый найденным сегментом длины k
одновременно в кучу добавлять k-1
и k-2
.
Тогда наименьший элемент в куче после всех операций и будет ответом.
// Function to find the maximum special substring length for a given character.
fn solve_char(data: &[u8], ch: u8) -> i32 {
let mut segments = BinaryHeap::new();
let mut i = 0;
while i < data.len() {
if data[i] == ch {
let mut length = 1;
// Count consecutive characters matching `ch`
while i + length < data.len() && data[i + length] == ch {
length += 1;
}
// Push adjusted lengths into the heap
for k in 0..3 {
if length <= k {break}
if segments.len() >= 3 && Reverse(length - k) >= *segments.peek().unwrap() {break}
segments.push(Reverse(length - k));
}
// Maintain only the top 3 lengths
while segments.len() > 3 {
segments.pop();
}
i += length; // Skip the processed segment
}
i += 1;
}
// Return the third-largest value if we have 3 elements, otherwise -1
if segments.len() == 3 {
match segments.pop().unwrap() {
Reverse(l) => l as i32,
}
} else {
-1
}
}
перейти
Ну и добавлю без объяснений более короткое решение, с использованием продвинутых битовых хаков, которое стоит попробовать осмыслить самостоятельно.
impl Solution {
pub fn minimize_xor(num1: i32, num2: i32) -> i32 {
let n1 = num1.count_ones();
let n2 = num2.count_ones();
let mut result = num1;
for _ in n1..n2 {
result |= result + 1
}
for _ in n2..n1 {
result &= result -1
}
result
}
}
перейти
Кажется что мы разговариваем на сильно разных английских.
По моему опыту:
- slayer вышел из употребления, последние пару раз я видел это слово в названии группы
- triggerman, manslayer вообще ни разу в жизни не встречал
- cutthroat до сегодняшнего дня был уверен что это исключительно прилагательное
- insecurity имхо к ревности имеет крайне опосредованое отношение, оно скорее про низкую самооценку и недостаток признания
- intelligentsia видел только применительно к СССР, в дискуссиях о США это скорее cultural elite/educated class/chattering class
перейти
разрешите поделиться своим решением начальник!
use std::collections::HashSet;
use std::ops::Sub;
impl Solution {
pub fn find_champion(n: i32, edges: Vec<Vec<i32>>) -> i32 {
let players = (0..n).collect::<HashSet<_>>();
let loosers: HashSet<_> = edges.iter().map(|it| it[1]).collect();
let new = players.sub(&loosers);
if new.len() != 1 {
return -1;
}
new.into_iter().next().unwrap()
}
}
перейти
Fun fact, ставящий под сомнение осмысленность модели: ее не попробовали ни на каких реальных данных. Как мне кажется, вариантов три:
- оно настолько бесполезное что даже на мнисте не завелось
- завелось, но результаты такие что стремно показать
- просто забили - неправдоподобная версия(ну странно же придумать новую архитектуру, описать ее теоретическое обоснование преимуществ и не попробовать ее ну хотя бы на паре первых попавшихся датасетов с kaggle)
перейти
Вот тут чувак попробовал как раз ряды Фурье https://news.ycombinator.com/item?id=40222212
В целом насчет хайпа вокруг KAN даже не знаю, статья выглядит прямым нарушением принципа the bitter lesson: давайте перейдем на что-то не GPU-friendly ради выигрыша в размере модели.
перейти
опять же поделюс своим решением тоже!
хотя оно не такое оптимальное судя по всему)
use std::iter::zip;
impl Solution {
pub fn can_change(start: String, target: String) -> bool {
{
let s1 = start.chars().filter(|it| *it != '_').collect::<String>();
let s2 = target.chars().filter(|it| *it != '_').collect::<String>();
if s1 != s2 {
return false;
}
};
let lindices1 = Self::collect(&start, 'L');
let rincides1 = Self::collect(&start, 'R');
let lindices2 = Self::collect(&target, 'L');
let rindices2 = Self::collect(&target, 'R');
for (l1, l2) in zip(lindices1, lindices2){
if l1 <= l2{
return false
}
}
for (r1, r2) in zip(rincides1, rindices2){
if r1 >= r2{
return false
}
}
true
}
fn collect(s:&str, target:char)->Vec<usize>{
s.char_indices().filter(|(_, ch)|*ch == 'L').map(|it|it.0).collect()
}
}
перейти
Я освободил свой разум и понял первое решение. По-моему, его не стыдно и на собесе написать (разве что не в таком стиле, а циклом), оно не эзотерическое.
перейти
не короткое, но советую - "Факап" Харитонова, весьма качественный фанфик-полемика со Стругацкими(конкретно миром полудня), Рубидий(аналогично с ПНВС)
перейти
Интересно, в каких компаниях такие ужасы спрашивают. У меня платного leetcode нет, не подскажешь, если у тебя есть, конечно?
перейти
А в Rust нет аналога std::nth_element? Можно было бы от klogk избавиться (не то чтобы это на практике очень важно, конечно)
перейти
"Тройной контакт", Юдковски
"Привключения пилота пиркса", Лем - если вдруг еще нет
перейти
Привычное итеративное решение, конечно, стоит также привести для сравнения
impl Solution {
pub fn longest_nice_subarray(nums: Vec<i32>) -> i32 {
let mut current_bitset = 0;
let mut max_len = 0;
let mut left = 0;
for (right, &val) in nums.iter().enumerate() {
// Shrink the window if nums[right] conflicts with current_bitset
while current_bitset & val != 0 {
current_bitset ^= nums[left];
left += 1;
}
// Expand the window by including nums[right]
current_bitset |= val;
max_len = max_len.max((right - left + 1) as i32);
}
max_len
}
}
перейти
- slayer книжное слово, и, мне кажется, ничем в этом смысле не отличается от "душегуба" и "головореза". В GoT кличку Kingslayer не снабжали сноской и не поясняли
- я не знаю, как перевести "он ревнует свою жену ко всем подряд" с сохранением смысла без использования слов insecurity/insecure. Со словом jealousy/jealous получается совсем другое утверждение, что-то вроде "он (думает, что) не получает от своей жены того, что достается всем подряд, и от этого страдает"
- intelligentsia в мемах пример 1, пример 2. Это я сейчас поиском нашел, но вообще на сабстеке периодически натыкался в живой природе
перейти
Понял, что не понимаю твоей позиции. Делать некоторые скрининги обязательными + настойчивое предложение делать аборт или даже поощрение за него это, кажется, не евгеника (ну или много в каких странах уже давно евгеника) и не супер спорно. Проблема в том, что скрининги довольно мало для чего бывают. Работающий скрининг для РАС это точно задача на миллиарды, первооткрыватель ДНК занимался ей целым институтом и не осилил, например.
Обычно под евгеникой понимается, что "людям с некоторыми особенностями, сильно коррелирующими с больным/плохим потомством, запрещаем иметь детей или настойчиво стимулируем их не иметь". Вот тут мое сильное мнение, что это невозможно делать "чуть-чуть", слишком slippery slope, и в итоге она получится как раз "в стиле нацистов".
перейти
Выглядит как хайп ради хайпа. Также как было с микро черными дырами от Большого Андронного Коллайдера.
То, что бактерию никто не сможет съесть вообще не означает, что она будет размножаться свободно. Как минимум конкурентная борьба за ресурсы с более приспособленными к борьбе обычными бактериями её сильно ограничит.
Ну и касательно ресурсов. Такая бактерия, как я понимаю, скорее всего будет хемотрофной, а вот для современных хемотрофов ниши весьма специфичны - ни разу не серая слизь по описанию.
перейти
Например, сценарист - Джонатан Нолан (брат режиссера и сценарист фильмов "Мементо", "Престиж", "Темный рыцарь", "Интерстеллар").
Абстрагируясь от поисков глубокого смысла, просто смотреть интересно, а в киношке это главное. Стилистика свежая и необычная (кровь-кишки-постапокалипсис на фоне радостного солнечного дня), куча мелких деталей большого мира. Конечно, нужно сказать спасибо исходной вселенной, но всё это как минимум не потеряли.
перейти
Даже в этом решении, если заглянуть глубже split_whitespace
не массив слайсов возвращает, а итератор по ним
sruct.SplitWhitespace. В спецификации его характеристики по дополнительной памяти не уточняются, но вполне возможно реализовать такой итератор на константных костах. И можно обсудить как сделать итератор с такими гарантиями самостоятельно.
перейти
Согласен, аналогичная история.
Мое доказательство основано на тождестве 0-0=0. Допустим, наше решение не оптимально. Рассмотрим первый несовпадающий чанк в нашем решении и в оптимальном. Оба имеют нулевой баланс. В нашем решении он короче, потому что иначе мы сформировали бы его раньше. Тогда чанк в якобы оптимальном решении можно разбить на два -- наш и остаток. Пришли к противоречию.
перейти
Ненавижу такие задачи. Алгоритмически она тривиальная, но, конечно же, в условиях таймпрессинга я с какой-то вероятностью забуду про спецслучай нуля. Это ничего не говорит не только о моем умении программировать, но даже о моем умении решать литкод. Да, в жизни тоже бывают спецслучаи, про которые нельзя забывать, и это часть работы. Но не такие!
перейти
Ты прав, так можно!
В Rust - этот метод называется немного по уродски, но есть: select_nth_unstable.
Есть и проще метод - нужно просто 3 итерации BubbleSort'a прогнать и они гарантируют 3 наибольших значения на своих местах.
перейти
Стоит также обратить внимание на чуть более медленное, зато короткое решение через стабильную сортировку
impl Solution {
pub fn pivot_array(mut nums: Vec<i32>, pivot: i32) -> Vec<i32> {
nums.sort_by_key(|&num| num.cmp(&pivot)); nums
}
}
перейти
Достаточно просто показать, что при увеличении числа студентов в классе margin gain
только падает. А это в свою очередь означает, что любая цепочка добавлений студентов в разные классы может быть отранжирована по убыванию margin gain
и корректно применена уже в новом порядке.
перейти
Для максимизации среднего соотношения нужно жадно добавлять студентов
Почему? Это совершенно не очевидное математически утверждение. У нас тут какая-то очень ядреная функция от n переменных, почему жадным методом мы придем в её глобальный минимум, а не в локальный?
перейти
В реальном собеседовании поверх такой задачи будет доп.требование "...без выделения доп.памяти", тогда у нее появляется понятный смысл -- проверить умение человека не запутаться в двух индексах и краевых условиях.
перейти
@
finder
Посмотри, пожалуйста, почему обещаемая в документации "быстрая ссылка на википедию" не срабатывает у меня:
[[Алгоритм_Кнута_—Морриса—_Пратта]]
перейти
Я, видимо, туплю, хеш-таблицы-то зачем? Двух массивов хватило бы?
перейти
Хм, здесь появилась жизнь! Сертификат обновил =)
перейти
Нужно было начать с чата в Телеграме!
перейти
С БАК было более очевидно -- емнип был простой контраргумент, состоящий в том, что энергия столкновения все еще меньше энергии некоторых (редких, но время от времени вполне регистрируемых) космических частиц, входящих в атмосферу Земли. Т.е. аналогичные события в природе происходят "сами собой", нет ничего особенного конкретно в БАК.
В случае с бактерией неправильной хиральности есть небессмысленная модель угроз: в биосфере появляется незамкнутая цепочка переработки (бактерия вряд ли сможет замкнуть все циклы в одиночку). Подобное в реальной истории жизни на Земле уже несколько раз происходило, например, carboniferous period. В результате происходит накопление неиспользуемых веществ и вывод их из активного оборота до тех пор, пока не эволюция не произведет кого-то, кто умеет их перерабатывать, что может занять десятки миллионов лет. Для этого не обязательно, чтобы новая бактерия была "суперхищником", достаточно, чтобы она просто быстро не сдохла.
Ну и в целом соотношения risk-reward тут крайне сомнительные.
перейти
Кто здесь? :)
перейти
Опять я провалил АА, что ж такое-то.
Если серьезно, то это пример, когда литкод-стайл измеряет не просто не то же самое, что нужно потом на работе, а прямо противоположную вещь. Сама задача очень простая, но, оказывается, можно ошибиться вот так. При этом в работе думать о том, что фраза "Даны шарики с уникальными метками от 0 до limit (всего limit + 1 шариков)" может означать, что их может оказаться миллиард, натурально вредно. Речь не о том, что не нужно оптимизировать заранее (иначе пол-литкода можно было бы стереть), но ограничения на количества объектов и операций обычно читаются из физического смысла происходящего. Запросов или, там, транзакций может быть миллиард, пользователей уже вряд ли, если вы не Мета, а шариков будут десятки, может, сотни. Особенно если они перенумерованные (а не снабженные, скажем, guid-ами).
перейти
А, и правда. Я не пишу на Rust и не знал, что там split возвращает итератор.
Но я больше не про память, а про то, что в текущей постановке задача почти что fizzbuzz: проверяет, что кандидат знает синтаксис языка и то, как в нем обращаются со строками в стандартной библиотеке. "Дальше можно обсудить" это понятно, но если в процессе такого обсуждения код не писать, то проверка, что человек не путается в индексах и знает, что делать со строчками, заканчивающимися на пробел, не состоится. Имхо лучше, если задача сразу так поставлена, чтобы она возникала естественным образом.
перейти
А, я понял.
То есть речь о рисках небыстрого, но полного занятия какой-то биоподобной ниши.
С таким я согласен, возможно.
Но всё же, я также и почти уверен, что заниматься эта ниша будет даже не годами, а тысячелетиями или десятками тысяч лет. Это совсем копейки с точки зрения геологической летописи, но совсем иной уровень угроз, с точки зрения человеческой цивилизации (мы тупо парниковыми газами успеем раньше всё загадить).
перейти
Мне на самом деле практика именно задач через LeetCode не очень нравится тем, что тесты уже даются системой.
Интересная часть, в которой кандидат сам придумывает крайние случаи для своего кода исключаются.
Я понимаю, с другой стороны, что это идёт в угоду сокрашения времени. Меж тем это всё-равно самая важная часть отладки на мой взгляд, позволяющая отсечь тех кто программирует сам, от использующих ChatGPT.
перейти
2.Я про развитый мир, но в целом и в африке сейчас выживаемость выше чем скажем 100 лет назад
3. Так не надо же всех отклоняющихся скопом. Надо смотреть куда ведет это отклонение(если известно что этот ген кореллирует с низким IQ, инвалидностью или типа того - может и не нужен нам такой ген в популяции?)
Ну а с 1 сложно - потому что у эволюции нет цели, тут сложно сказать "правильно/неправильно"
перейти
Меня в целом заинтересовало больше утверждение о более интерпретируемых для человека нейросетевых моделях. Понятно, что пока адекватного тулинга в этой области нет, но перспектива его появления прям обнадёживает.
Вполне могу поверить в адекватность мнения о том, что трейдофф между скоростью и интерпретируемостью полезен для сообщества.
перейти
Как будто бы, ты слишком строк к себе!
Не понять интервьюера не является грехом. Важно же, просто уметь быстро перестраивать своё решение, если что-то было изначально понято неправильно. И как раз обладание таким скилом, на мой взгляд, является гипер-полезным в рабочем процессе. И отлично, если ты его сумел показать на собеседовании!
перейти
Да, вы правы, я просмотрел статью, не нашел упоминаний, и решил что не тестировали. К этому комменту тоже много вопросов, конечно, например то что равное число нейронов взято, а не равное число параметров - тут kan может побеждать тупо за счет того что кан плотнее в количество точек раз, но это уже что-то
перейти
В первом решении - самое сложно на мой взгляд - доказать корректность.
Не то, чтобы это было почти невозможно, возможно и за разумное время.
Но на доказательство, почему он работает (и в том числе безуспешные попытки найти контрпример) у меня ушло больше времени, чем на написание/понимание кода.
перейти
Скорее было бы "делайте скрининг во время беременности", а потом варианты от "делайте аборт"(приказ) до "может лучше сделать аборт?"(совет + мб поощрение)
Запрещать иметь детей - это уже евгеника в стиле нацистов, такое никому не нужно
перейти
В ограничениях задачи - шариков может быть до миллиарда: никто не гарантирует, что все бесцветные шарики в памяти уложатся.
В подходе с хэш-таблицей мы только цветные шарики храним в памяти, а их не больше, чем число операций.
перейти