mix666
@mix666

Какую формулу использовать?

Есть алгоритм, который считает токены.

Выглядит так:

User 1 закинул в котёл 100 монет А и 10 монет Б.
User 2 закинул в котёл 200 монет А и 20 монет Б.

Мы складываем все монеты и даём юзерам токены равные сумме этих монет
User 1 = 110 токенов
User 2 = 220 токенов

Общий котёл токенов = 330 токенов

Теперь нам надо высчитать, сколько user 1 имеет монет исходя из его токенов:

110 (токенов user 1) / 330 (все токены котла) = 0,33333333

Берём все монеты в котле и умножаем на 0,33333333
300 * 0,33333333 = 99.99999999

Получается 99.99999999

Ответ не совпадает, так как у user 1 было 100 монет, а получаем мы 99.99999999

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

Есть пример на GOLang: https://go.dev/play/p/6ycWbK1Dans

Код примера
package main

import (
	"fmt"

	"github.com/shopspring/decimal"
)

func calculateCoins(userTokens, totalTokens, totalCoins decimal.Decimal) decimal.Decimal {
	ratio := userTokens.Div(totalTokens)
	userCoins := ratio.Mul(totalCoins)
	return userCoins
}

func main() {
	user1_CoinA := decimal.NewFromFloat(100)
	user1_CoinB := decimal.NewFromFloat(10)

	user2_CoinA := decimal.NewFromFloat(200)
	user2_CoinB := decimal.NewFromFloat(20)

	user1_Tokens := user1_CoinA.Add(user1_CoinB)
	user2_Tokens := user2_CoinA.Add(user2_CoinB)

	totalTokens := user1_Tokens.Add(user2_Tokens)
	totalCoinsA := user1_CoinA.Add(user2_CoinA)

	user1Coins := calculateCoins(user1_Tokens, totalTokens, totalCoinsA)
	user2Coins := calculateCoins(user2_Tokens, totalTokens, totalCoinsA)

	fmt.Println("User 1 has", user1Coins.String(), "coins")
	fmt.Println("User 2 has", user2Coins.String(), "coins")
}
  • Вопрос задан
  • 115 просмотров
Пригласить эксперта
Ответы на вопрос 3
монеты могут быть в значении 0.0005

Если это - минимальное значение, то перед началом расчётов домножаете всё на 10000 и далее спокойно округляете, без всякой арифметики с плавающей точкой.
Ответ написан
Комментировать
wataru
@wataru Куратор тега Алгоритмы
Разработчик на С++, экс-олимпиадник.
Можно делить на цело в конце. 110*300/330 даст ровно 100. Если же в котле осталось не делящееся нацело количество монет, то будет округление вниз: для 301 монеты тоже получится 100 вместо 100.(3).

Надо хранить долю юзера в виде рационального числа - отдельно числитель и знаменатель.
Ответ написан
Комментировать
Adamos
@Adamos
Деление 17 монет на троих:
15 монет делим на троих поровну, 2 монеты отдаем первому и второму из скидывавшихся.
Не идеально справедливо, зато без бесконечных дробей, которые иначе неизбежны.
При неравных долях сортируем скидывавшихся так, чтобы раньше получал внесший большую долю.
Собственно, при этом должна восстановиться исходная картина - как же так, скидывались поровну, а в сумме 17 ;)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы