Pull to refresh
90
0
Сергей Шашков @ShashkovS

Менеджер продукта, методист, разработчик

Send message

Могу сказать как краевед, который считал эти копейки много лет (и как математик).
Ни в коем случае нельзя считать проценты на остаток добавляя какую-то дополнительную точность (условно 3 знака после запятой): всё разъедется через некоторое время с вероятностью 100%.

Правильное вычисление выглядит так:
Нужно каждый раз считать полную сумму с нужной итоговой разрядностью «как бы заново» и вычитать предыдущее значение.

Условно, если у нас на вкладе 10.00 рублей и ставка, скажем, 16.5%, и прошло, условно, 17 дней месяца, то должно быть добавлено 10.00 * 16.5 * 17 / 100 / 365 (или 366 в високосный год).
Вот эта штука 10.00 * 16.5 * 17 / 100 / 365, если в результате нужно округлить к целому по математическим правилам, считается точно при помощи несложной арифметики в целых числах.
Получится 0.08 (8 копеек). Значит после 17 дней, у нас 10.08.
После 18 дней слова будет 10.08. А вот уже после 19-го дня будет 10.09 рублей.

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

Код не обфусцирован, просто открывайте и читайте

Уж лучше через связывание в параметр по умолчанию. Читается получше...

Там мультиподпись. Нужно передать "файлик" через всех подписывающих. Там же наверняка "серьёзные" дяти, не через тележечку они его перекидывают? Значит, есть какой-то программный интерфейс для передачи его по кругу. И на этот интерфейс можно совершить атаку.
Может и не веб, конечно, но веб просится первым.

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

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

С современным вебом с тысячими завимостей под капотом представить себе атаку на веб-приложение легко. Про первую часть с атакой на этап создания транзакции не очень понятно. Но если там тоже веб...

Немного другое решение (на самом деле близкое к поиску циклов, потому как их очень похожим образом можно искать).

Сначала делаем обход в глубину с отметаками времени входа-выхода для дорог одного типа.

Это позволяет за O(1) отвечать на вопрос "является ли эта вершина предком этой", что для нашего графа эквивалентно "если ли из первой ж/д первого типа во вторую".

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

А где хоть какое-то сравнение производительности по сравнению с однопоточным вариантом?

Ну а куда они могут без юр.лица в РФ и без "помощи" визы или мастеркарда деть сунуть рубли?
Хотя бы по ip не банят --- уже хорошо.

Это --- временное ограничения из-за атаки на их инфрастуктуру.

У вас устаревшие данные.

import os
import sqlite3
import time
import multiprocessing
import uuid


def writer_process(proc_id, num_inserts, db_file):
    conn = sqlite3.connect(db_file)
    conn.execute("PRAGMA busy_timeout = 5000")
    conn.execute("PRAGMA synchronous = NORMAL")
    for _ in range(num_inserts):
        with conn:
            conn.execute("INSERT INTO test_data (val) VALUES (?)", (str(uuid.uuid4()),))


def run(num_processes, num_inserts, test_id=1):
    start_time = time.perf_counter()
    db_name = f'db{test_id}.sqlite'
    conn = sqlite3.connect(db_name)
    conn.execute("PRAGMA journal_mode = WAL")
    conn.execute("CREATE TABLE IF NOT EXISTS test_data (id INTEGER PRIMARY KEY, val TEXT)")
    conn.commit()
    conn.close()

    processes = []
    for i in range(num_processes):
        p = multiprocessing.Process(target=writer_process, args=(i, num_inserts, db_name))
        processes.append(p)

    print(f'Тестируем {num_processes} процессов')
    for p in processes:
        p.start()

    for p in processes:
        p.join()

    os.unlink(db_name)

    total_time = time.perf_counter() - start_time
    total_inserts = num_processes * num_inserts
    print(
        f"{num_processes} процессов. Общее время: {total_time:.4f} секунд. Суммарная скорость: {total_inserts / total_time:.2f} транзакций/сек")


if __name__ == "__main__":
    num_inserts = 277200
    for num_processes in range(1, 21):
        run(num_processes, num_inserts // num_processes)

У меня максимум на 4 одновременных процессах --- 70К транзакций в секунду.
Дальше до 20 процессов --- около 30К транзакций в секунду. И это в режиме, когда все процессы только и делают, что пишут в одную общую нагруженную таблицу.

Если вы пишете асинхронный вебсервис, то там будет столько воркеров, сколько у вас физических ядер. То есть до 20 воркеров --- всё ок, это достаточно высокая нагрузка.
Не high load, конечно, но..

Более того: Т-банк по комиссиям не присылает уведомлений (никаких и нигде). Их можно обнаружить только в выписке по своей прямой инициативе. Так что среднее число оплат комиссии на человека будет гораздо больше единицы.
Бизнес, ничего личного.

Про API так вообще очевидно: они для доступа к API используют библиотеку openai, и типичный код выглядит так:

from dotenv import dotenv_values
from openai import OpenAI

config = dotenv_values(".env")

BASE_URL = "https://api.deepseek.com/beta"
MODEL = "deepseek-chat"

client = OpenAI(api_key=config['API_KEY'], base_url=BASE_URL)

response = client.chat.completions.create(
    model="deepseek-chat",
    messages=[
        {"role": "system", "content": "You are a helpful assistant"},
        {"role": "user", "content": "Hello"},
    ],
)

print(response.choices[0].message.content)

То есть их публичные API совместимы с OpenAI'шными. Что просто супер-удобно, ведь имеющийся для OpenAI-код можно легко применить к их модели.

Ой, ещё как считают. У нас есть специальный чатик, куда мы double-дичь с денежками скидываем. Кажется уже пару десятков примеров.

Ой, да, спасибо, поправил.
Но в целом оно криво работает только на экстремальных числах.
То есть в условном банке или при расчёте денег такое писать не нужно: обязательно на копеечку когда-нибудь разъедется. А если считаешь пиксели на экране, то идеально.

Строго говоря x+0.5 не всегда работает правильно.

> round = (x) => Math.floor(x + 0.5);
function round(x)

> x = 0.49999999999999994;
0.49999999999999994
> x
0.49999999999999994
> Math.round(x)
0
> round(x)
1 

Вот, кстати, решение на Python через эту формулу. Только оно ощутимо медленнее:

Код на Python
from gmpy2 import mpz


class R5:
    def __init__(self, a=0, b=0):
        self.a = mpz(a)
        self.b = mpz(b)

    def __repr__(self):
        return f'R5({self.a}, {self.b})'

    def __str__(self):
        if self.a and self.b:
            return f'({self.a}{self.b:+}√5)'
        elif self.b:
            return f'{self.b}√5'
        else:
            return f'{self.a}'

    def __add__(x, y):
        return R5(x.a + y.a, x.b + y.b)

    def __sub__(x, y):
        return R5(x.a - y.a, x.b - y.b)

    def __mul__(x, y):
        return R5(x.a * y.a + x.b * y.b * 5, x.a * y.b + x.b * y.a)

    def __pow__(x, power):
        if power == 0:
            return R5(1, 0)
        elif power % 2 == 1:
            return x * (x ** (power - 1))
        else:
            sq = x ** (power // 2)
            return sq * sq

    def __floordiv__(x, n):
        n = mpz(n)
        return R5(x.a // n, x.b // n)


def fib_bine(n):
    return (R5(1, 1) ** n - R5(1, -1) ** n).b // mpz(2) ** n


from time import perf_counter
for i in range(1, 10):
    print(i, fib_bine(i))

N = 100_000_000
st = perf_counter()
y = fib_bine(N)
en = perf_counter()
print(f'{en - st:.4}с')
# 1_000_000_000 — 81.16с

PEP8 с рекомендацией 79 — это от 2001-го года. С мотивацией

The limits are chosen to avoid wrapping in editors with the window width set to 80

Сейчас такие терминалы если и есть, то их почти никто не использует. Ну и 100%, что дети не будут писать код, который будут на них читать.

Современные мониторы — широкие, а не высокие. Дорого место по высоте, а не по ширине.

Лишние переносы там, где они по смыслу не нужны, только затрудняют чтение. Ширина 79 заставляет их использовать в декларациях функций, в длинных строках (там где 'blah-blah') и т.п. — там, где с ними хуже, чем без них.

Что это? Для кого это? Ну, то есть я понимаю, что статья — реклама «школы для детей», но для кого статья?
Кто пишет красивый код? Дети? Дети с нуля пишут

class Test():
def init(self) -> None:
pass

? Или взрослые пишут код, который читают дети?

Ограничивайте длину строки 79 символами.

В 2024-м? Правда?

В общем в статье безумная смесь тройки цитат из PEP8, стандартных клише-блоков «зачем детям python» и «где учить питон» и ссылка с рекламной кампанией на лендос.

Паскаль (как и честный python, c#, js и т.п.) — это не про 7-8 лет, и даже не про 10-11-12, если говорить о массах: рано, сложно.
Началка — это что-то полу-игровое: цель не научить языку, а научить думать «как программист»: какие нужны шаги, как исправить ошибку и т.п.

Лично я не понимаю, зачем в «обычной» школе учить язык, на котором школьник не сможет решить ни одной своей «настоящей» задачи. Для тех двоих, кто станет айтишниками, начало с паскаля может и принесёт пользу, для остальных — вряд ли.
ИМХО примерно как начинать обучение вождению с управлению ВАЗ2106 («шестёрка») с обязательным залезанием под капот и самостоятельным ремонтом каждый раз, когда что-то перестаёт работать. Зато близко к железу...

Современное «под капотом» авто настолько сложно, что большинство водителей не то, что не понимает, как оно устроено, даже просто не сможет назвать функции хоть каких-нибудь 10 агрегатов.

PS. Я пока только 11 лет учу школьников. Сам начинал с бейсика и паскаля, но сейчас я уверен, что языки, в которых простые вещи делаются сложно, скорее убивают мотивацию, чем воспитывают таланты.

1
23 ...

Information

Rating
10,046-th
Location
Россия
Date of birth
Registered
Activity

Specialization

Backend Developer, Product Manager
Lead
Python
Project management
Algorithms and data structures
asyncio