Losowy podział liczby

0

Próbuję napisać program który wczytuje ze standardowego wejscia dodatnia liczbe całkowita n, po czym
wypisuje na standardowe wyjscie n losowych liczb nieujemnych, które daja w sumie jeden. Jak pokazują moje wypociny poniżej mam kłopot z pętlą, ponieważ nie wiem jak uczynić tą pętle by wybierała z tych losowych liczb te "n" liczb które wypisaliśmy na starcie które w sumie dają jedynkę

#include <iostream>
#include <ctime>
#include <cstdlib>

int main()
{
	std::srand(std::time(nullptr));
	int number;
	std::cin >> number;
	double  x = (double)std::rand() / RAND_MAX;
	double sum = 0.;
	int i = 0;
	if (number > 0)
	{
		if (sum != 1) {
			for (; number > i; ++i)
			{
				sum += x;

				if (sum == 1)
				{
					continue;
				}
			}
			
		}
		std::cout << sum << std::endl;
	}

	
}

1

Na zmiennym przecinku bez porównywanie na równośc jest niemal bez szans.
https://pl.wikipedia.org/wiki/Liczba_zmiennoprzecinkowa

To można ratować za pomocą porównywania z 'epsylon' (poszukaj na forum, jest dyskutowane co tydzień)

1
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>

int main()
{
    std::srand(std::time(nullptr));
    int number;
    std::cin >> number;
    double* v = new double[number];

    
    double sum = 0.;
    int temp;
    double suma = 0.;
    if (number > 0)
    {
        if (sum != 1) {
            for (int i=0; i<number; ++i)
            {
                temp = std::rand()/number;
                v[i] = temp;
                sum += temp;
            }
            for (int i = 0; i < number; ++i) 
            {
                v[i] /= sum;
                std::cout << v[i] << " ";
                suma += v[i];
            }

        }
        std::cout << std::endl<< suma << std::endl;
    }

}

Losuje n randomwych intów po czym je sumuje i dzielę przez sumę w konsekwencji czego mam pewność ,że mi wyjdzie 1. Maksymalną liczbę wylosowaną dzielę przez liczbę liczb po to by przy sumowaniu nie wyjść za zakres. Ale nie jestem pewien czy dobrze zrozumiałem problem. Jest to troszkę oszukana metoda bo nie dzielimy liczbę losowo, ale dostosowujemy wylosowane liczby pod to aby wyszedł odpowiedni wynik.

2

Trochę za mało danych.
Samo powiedzenie, suma ma być równa 1.0 daje bardzo dużych rożnych możliwości.
Zaczynając od zakresu możliwych wartości (np {-1, 2} też sumuję się do 1), a kończąc na oczekiwanym rozkładzie.
Poniższe nie daję jednorodnego rozkładu mimo użycia std::uniform_real_distribution:

template<typename R>
std::vector<double> randomValuesZeroOne(size_t n, R& random)
{
     std::uniform_real_distribution dist{ 0.0, 1.0 };

     std::vector<double> r;
     r.reserve(n);
     std::generate_n(std::back_inserter(r), n, [&random, &dist]() { return dist(random); });
     return r;
}

template<typename R>
std::vector<double> randomValusesWithSumOne(size_t n, R& random)
{
    if (n <=0) throw std::invalid_argument("sum of zero value can't be equal to one.");
    auto r = randomValuesZeroOne(n - 1, random);
    std::sort(r.begin(), r.end());
    r.push_back(1.0);
    std::adjacent_difference(r.begin(), r.end(), r.begin());
    return r;
}

Strategia powyższego jest prosta. Losuję miejsca podziału zakresu <0. 1>, sortuję te miejsca podziału i wyznaczam odległości miedzy podziałami, co stanowi wynik.

1

Jak to "losowych liczb nieujemnych dających w sumie jeden", to chyba jedyna droga, to losować w pętli n liczb i czekać, aż dadzą jeden, hej, zaraz, zaraz, ale to floaty. Jak dla mnie dziwne zadanie + jak wyżej.

3

Polecam:

Sam algorytm zrobiłbym taki: losuję n liczb z zakresu [0, x), sumuję je. Kolejne wylosowane wartości dzielę przez tę sumę i wychodzi mi sumarycznie jeden. Np, przy wylosowaniu 1,4,9 będzie to 1/14 + 4/14 + 9/14. Oczywiście zaokrąglenie zmiennoprzecinkowe to osobny problem.

1

Wersja działająca, ale nie tak ładnie losująca jak mogłoby to wyglądać:

#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>

int main()
{
    std::srand(std::time(nullptr));
    int number, isum;
    std::cin >> number;
    
    // generacja liczb losowych typu int i ich sumowanie
    std::vector<int> numbers(number);
    isum = 0;
    while(number --> 0) {
      	numbers[number] = std::rand() % 1000;
      	isum += numbers[number];
      	std::cout << number << ": " << numbers[number] << std::endl;
    }
    std::cout << "isum: " << isum << std::endl;
    
    // zamiana liczb losowych int na double - bez reszty
    int i = 0;
    double sum = 0.;
    double val;
    for(int i = 0; i < numbers.size(); i++) {
    	val = (i < numbers.size() - 1) ? ((double)numbers[i]) / isum : 1.0 - sum;
    	std::cout << i << ": " << val << std::endl;
    	sum += val;
    }

    // wynikowa suma
    std::cout << "sum: " << sum << std::endl;       
}

https://ideone.com/hlRMH4

Ostatnia wartość musi być obliczona przez odejmowanie.
Tak się przynajmniej robi w finansach i symulacjach.
Można to krócej napisać, ale nie mam warunków (robię to na kolanie).

1 użytkowników online, w tym zalogowanych: 0, gości: 1