Losowy podział liczby

Losowy podział liczby
Lernom
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 3 lata
  • Postów:8
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ę

Kopiuj
#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;
	}

	
}

AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:3561
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ń)


Bo C to najlepszy język, każdy uczeń ci to powie
BO
  • Rejestracja:około 6 lat
  • Ostatnio:7 dni
  • Postów:214
1
Kopiuj
#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.

edytowany 3x, ostatnio: Botek
Lernom
Jest szansa , że da się to zrobić beż użycia wektorów ? Bo działa tak jak powinno.
BO
Poprawiłem na wersję z tablicą zamiast vectora.
Lernom
Dzięki wielkie , już analizuję co tu się stało. :D
MarekR22
Moderator C/C++
  • Rejestracja:ponad 17 lat
  • Ostatnio:około 4 godziny
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:

Kopiuj
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.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 1x, ostatnio: MarekR22
lion137
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Postów:4946
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.


AK
Pisałem o epsyulonie w tym wątku. Być może temat by należało określić jako "najbliższy 1" albo "nie przekraczający 1". Szkoda, ze pytający bierze słaby udział w swoich własnych wątkach
lion137
Nie doczytałem:), gram w brydża online, to tylko odpisałem patrząc na pytanie.
Lernom
No chyba o to chodzi , że losuje te liczby "n" losowo do napotkania sumy n liczb które dają w sumie 1
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Szczecin
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.


vpiotr
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 3 lata
1

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

Kopiuj
#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).

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.