Консультация № 175103
10.12.2009, 15:11
35.00 руб.
10.12.2009, 15:45
0 23 4
Здравствуйте, уважаемые эксперты!

Помогите доработать код для создания массива из сгенерированных чисел.

Вот сам код, но надо, чтобы элементы не повторялись.

Dim i, j As Integer
Dim numbers(N) As Integer
Dim element As Integer

For i = 1 To N
numbers(i) = Int(N * Rnd()) + 1
ListBox1.Items.Add(numbers(i))
Next


Сам попытался сделать, но не получилось - элементы все равно повторяются. То, что получилось - в примечании. Про Вас переделать или написать код для генерирования чисел, чтобы они не повторялись.

Спасибо.

Приложение:
For i = 1 To N
mark:
repeated = 0

numbers(i) = Int(N * Rnd()) + 1

If i > 1 Then
For j = 1 To i
If numbers(i) = numbers(j) Then
repeated = 1
End If
Next
End If

If repeated = 1 Then
GoTo mark
End If

ListBox1.Items.Add(numbers(i))

Next

Обсуждение

давно
Посетитель
7438
7205
10.12.2009, 15:42
общий
Вам на каком языке надо-то?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Старший Модератор
17042
808
10.12.2009, 15:46
общий
Иванов Андрей Владимирович:
Вопрос перенесён как не имеющий отношения к программированию на C / C++.
Об авторе:
We have but faith: we cannot know;
For knowledge is of things we see;
And yet we trust it comes from thee,
A beam in darkness: let it grow.
-----
https://www.linkedin.com/in/andreynkuznetsov
https://www.researchgate.net/profile/Andrey_Kuznetsov11
http://www.researcherid.com/rid/K-8824-2014
Неизвестный
10.12.2009, 15:57
общий
10.12.2009, 16:34
это ответ
Здравствуйте, Иванов Андрей Владимирович.

Ваш код похож на Visual Basic, который я не слишком хорошо знаю. Но поскольку вопрос в разделе C/C++, то и пример программы в приложении приведен на C++. Заполнение массива псевдослучайными числами реализовано в виде отдельной функции, сгенерированный массив для проверки выводится на экран (а Вы можете делать с ним, что пожелаете).

Я проверял программу с Visual C++ 6.0. Генератор псевдослучайных чисел у microsoft довольно неплохой, так что за более чем 20 тестовых запусков, повторов не было ни разу (что ожидаемо, выборка из 15 чисел маловата). Поэтому для проверки, действительно ли повторяющиеся числа отсеиваются, я заменял

int n = rand();

на (временно, убрано из конечного кода)

int n = rand() % 30; // псевдослучайное число от 0 до (30-1)

Это стандартный прием получения псевдослучайных чисел в заданном диапазоне.

Успехов!

Раз требуется на Visual Basic, a я дал ответ на C++, то вот Вам еще вариант на Visual Basic 6:

Файл 175103.frm
Код:
VERSION 5.00
Begin VB.Form Form1
Caption = "Form1"
ClientHeight = 4092
ClientLeft = 48
ClientTop = 324
ClientWidth = 2520
LinkTopic = "Form1"
ScaleHeight = 4092
ScaleWidth = 2520
StartUpPosition = 3 'Windows Default
Begin VB.CommandButton Generate
Caption = "Generate"
Height = 372
Left = 240
TabIndex = 1
Top = 3480
Width = 1932
End
Begin VB.ListBox List1
Height = 2928
Left = 240
TabIndex = 0
Top = 240
Width = 1932
End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Private Sub Form_Load()
Randomize
End Sub

Private Sub Generate_Click()
Dim i, j As Integer
Dim nums(20) As Integer
Dim found As Boolean
Dim tmp As Integer

List1.Clear
i = 0
While (i < 20)
again:
tmp = Int((30000 * Rnd) + 1)
found = False
For j = 0 To i - 1
If nums(j) = tmp Then
found = True
Exit For
End If
Next j
If found Then GoTo again
nums(i) = tmp
List1.AddItem (tmp)
i = i + 1
Wend
End Sub


По нажатию кнопки [Generate] список очищается, а затем заполняется 20-ю псевдослучайными числами.

Успехов!

Приложение:
#include <time.h> // для time()
#include <iostream> // Ввод-вывод на консоль
using namespace std;

const int max_count = 15;

// заполнение массива псевдослучайными числами
// p - указатель на массив
// count - число элементов
void fill_array( int* p, int count )
{
int i = 0;
while( i < count ) {
int n = rand(); // псевдослучайное число в диапазоне от 0 до RAND_MAX

// проверим на совпадение с уже сгенерированными
for( int j = 0; j < i; ++j ) {
if( p[j] == n ) // совпало
break;
}
if( j < i ) continue; // было совпадение, пытаемся снова

// уникальное число, сохраняем
p[i++] = n;
}
}


int main()
{
// инициализируем генератор псевдослучайных чисел текущим временем,
// так что числа должны быть различными при каждом запуске программы,
// если интервал был не менее секунды (time() возвращает время в секундах)
time_t t;
srand( (unsigned)time( &t ) );

int numbers[max_count];
fill_array( numbers, max_count );

// выводим на экран для проверки
for( int i = 0; i < max_count; ++i )
cout << "numbers[" << i << "] = " << numbers[i] << endl;

return 0;
}
Неизвестный
10.12.2009, 15:58
общий
>> Лысков Игорь Витальевич

Спасибо, что перенесли, в универе мало времени было на написание вопроса, потому и перепутал раздел.
Неизвестный
10.12.2009, 16:00
общий
>> amnick
Спасибо за ответ, но мне надо на VB.

Я немного покумекал, и получил код, который генерирует N - 1 элемент, но мне нужно, чтобы герерировали все элементы от 1 до N, но в произвольном порядке.
for i = 1 to N
numbers(i)=i
next
for i = 1 to N-1
j=int(Math.Floor((N-i+1)*rnd()))+i
if i<>j then
k=numbers(i)
numbers(i)=numbers(j)
numbers(j)=k
end if
next
давно
Профессор
230118
3054
10.12.2009, 16:03
общий
это ответ
Здравствуйте, Иванов Андрей Владимирович.
В программировании индекс массива начинается с 0. Цикл надо начинать с 0 или писать i-1. Кроме того, не надо сравнивать значение с самим собой, это у Вас происходит, когда j = i . Переработаннный код программы дан в приложении.

Приложение:
For i = 1 To N
mark:
repeated = 0

numbers(i - 1) = Int(N * Rnd()) + 1

If i > 1 Then
For j = 0 To i - 2
If numbers(i - 1) = numbers(j) Then
repeated = 1
End If
Next
End If

If repeated = 1 Then
GoTo mark
End If

ListBox1.Items.Add(numbers(i - 1))

Next
Неизвестный
10.12.2009, 16:19
общий
>> Ashotn
не помогло, элементы все равно повторяются.
Неизвестный
10.12.2009, 16:27
общий
Иванов Андрей Владимирович:
Раз требуется на Visual Basic, a я дал ответ на C++, то вот Вам еще вариант на Visual Basic 6:

Файл 175103.frm
Код:
VERSION 5.00
Begin VB.Form Form1
Caption = "Form1"
ClientHeight = 4092
ClientLeft = 48
ClientTop = 324
ClientWidth = 2520
LinkTopic = "Form1"
ScaleHeight = 4092
ScaleWidth = 2520
StartUpPosition = 3 'Windows Default
Begin VB.CommandButton Generate
Caption = "Generate"
Height = 372
Left = 240
TabIndex = 1
Top = 3480
Width = 1932
End
Begin VB.ListBox List1
Height = 2928
Left = 240
TabIndex = 0
Top = 240
Width = 1932
End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Private Sub Form_Load()
Randomize
End Sub

Private Sub Generate_Click()
Dim i, j As Integer
Dim nums(20) As Integer
Dim found As Boolean
Dim tmp As Integer

List1.Clear
i = 0
While (i < 20)
again:
tmp = Int((30000 * Rnd) + 1)
found = False
For j = 0 To i - 1
If nums(j) = tmp Then
found = True
Exit For
End If
Next j
If found Then GoTo again
nums(i) = tmp
List1.AddItem (tmp)
i = i + 1
Wend
End Sub


По нажатию кнопки [Generate] список очищается, а затем заполняется 20-ю псевдослучайными числами.

Успехов!
Неизвестный
10.12.2009, 16:36
общий
это ответ
В программе amnick может возникнуть ситуация, когда число элементов массива больше RAND_MAX (32767) . Тогда невозможно будет сгенерировать массив из не повторяющихся элементов и программа зависнет.
Привожу свой код ниже.

Приложение:
#include <iostream>
#include <ctime>
#define dSIZE 200 //здесь определяем максимальный размер массива
#define dRand 32767 //максимальное генерируемое число
using namespace std;

int main(int argc, char *argv[])
{
int size;
int a,i,j;
time_t t;
int massiv[dSIZE];
setlocale(LC_ALL, ".1251"); //для правильного вывода русских букв
for(i=0;i<dSIZE;i++) massiv[i]=0; //обнуление массива
if (argc < 2)
{
cout << "В качестве аргумента введите размер массива" <<endl;
cout <<argv[0]<<" <размер массива>"<<endl;
return -1;
}
else
{
size=atoi((const char*)argv[1]); //преобразование строки в число
if (size>dSIZE)
{
cout<<size<<" > "<<dSIZE<<endl;
return -1;
}
if (size>dRand)
{
cout<<size<<" > "<<dRand<<endl;
cout<<"Нельзя сгенерировать массив без повторных чисел"<<endl;
return -1;
}
srand((unsigned)time(&t)); // для генерации псевдослучайных чисел
for (i=0; i<size; i++)
{
newRand:
a = rand()%dRand; //случайное число от 0 до dRand
for (j=0; j<i; j++)
{
if (massiv[j]==a) goto newRand; //если такое число уже есть в массиве, генерируем другое число
}
massiv[i]=a; //заносим сгенерированное число в массив
}

//вывод массива на экран
for (i=0;i<size;i++)
cout<<massiv[i]<<" ";
}
}
давно
Профессор
230118
3054
10.12.2009, 17:46
общий
Иванов Андрей Владимирович:
Такого не может быть. Программа правильная.
давно
Старший Модератор
17042
808
10.12.2009, 20:10
общий
Товарищи, Вы хоть обращайте внимание на тему рассылки и язык программирования в вопросе. Если первый ответ можно понять: автор вопроса не туда его подал, но третий ответ... К чему тут ответ на C++, когда нужен Visual Basic?
Об авторе:
We have but faith: we cannot know;
For knowledge is of things we see;
And yet we trust it comes from thee,
A beam in darkness: let it grow.
-----
https://www.linkedin.com/in/andreynkuznetsov
https://www.researchgate.net/profile/Andrey_Kuznetsov11
http://www.researcherid.com/rid/K-8824-2014
Неизвестный
11.12.2009, 06:14
общий
Оффтоп

2 Dr_Andrew: это из области: "хочешь сделать человека сытым, дай ему рыбу, хочешь сделать человека счасливым - научи его ловить рыбу".
Так и тут, "хочешь сделать человека счасливым - научи его с++"
Неизвестный
11.12.2009, 06:18
общий
это ответ
Ладно, читаем msdn:

Before calling Rnd, use the Randomize statement without an argument to initialize the random-number generator with a seed based on the system timer.

To produce random integers in a given range, use this formula:

Код:
Int((upperbound - lowerbound + 1) * Rnd + lowerbound)

Here, upperbound is the highest number in the range, and lowerbound is the lowest number in the range.
Неизвестный
11.12.2009, 06:47
общий
Иванов Андрей Владимирович:
Это можно записать гораздо короче. Код не проверял, поэтому посылаю в форуме.
Код:
For i = 1 To N
generate:
numbers(i) = Int(N * Rnd()) + 1
For j = 1 To i -1
If numbers(i) = numbers(j) Then
goto generate
End If
Next j

ListBox1.Items.Add(numbers(i))
Next i


Кроме того, в строчке Dim i, j As Integer - i имеет тип не целое, а variant. Каждой переменной надо указывать конкретный тип. Иначе будет назначен тип по-умолчанию (variant).
Неизвестный
12.12.2009, 11:32
общий
Эксперты, отвечавшие на данный вопрос, мягко говоря, странные.
Во-первых, зачем мне код на C++? Первого отвечавшего еще можно понять, но когда вопрос был перенесен в правильный раздел, и все равно писали на С++ - видимо некоторые эксперты не очень дружат с головой, а еще платными называются - безобразие!
Во-вторых, коды, которые предоставили на VB очень замудреные и не совсем работают. У меня у самого гораздо лучше получилось...

Так что всем экспертам - Низачот!

Вот, как код должен выглядеть:

For i = 1 To N
numbers(i) = Int(N * Rnd()) + 1
repeated = False
If i > 1 Then
For j = 1 To i - 1
If numbers(i) = numbers(j) Then
repeated = True
End If
Next
End If
If repeated = True Then
i -= 1
Else

End If
Next
давно
Профессор
230118
3054
12.12.2009, 12:46
общий
Иванов Андрей Владимирович:
Прежде чем давать оценки, советую еще раз внимательно разобраться. Данную оценку Вы уже не сможете исправить.
Еще раз напоминаю, что индексы массива начинаются с 0. Как минимум память, отведенная под нулевой элемент, пропадает.
давно
Профессор
230118
3054
12.12.2009, 13:05
общий
Иванов Андрей Владимирович:
Код Evgenijm, кстати, тоже правильный.
давно
Профессор
230118
3054
12.12.2009, 13:35
общий
Иванов Андрей Владимирович:
Еще замечание: изменять значение переменной цикла в цикле опасно. Это затрудняет понимание и отладку программы. С другой стороны, хорошо, что Вы избавились от Goto. Но после того, как нашли повтор, из цикла программа не выходит. То есть она совершает лишние вычисления и тратит время. Можем использовать замечательный оператор Exit For.
Поэтому предлагаю этот вариант.
numbers(0) = Int(N * Rnd()) + 1
For i = 1 To N - 1
repeated = True
While repeated = True
numbers(i) = Int(N * Rnd()) + 1
For j = 0 To i - 1
If numbers(i) = numbers(j) Then
repeated = True
Exit For
End If
Next
If j > i - 1 Then
repeated = False
End If
End While
Next
Неизвестный
12.12.2009, 13:42
общий
Ashotn:
В VB6 это зависило от настроек. В распространенных Бейсиках индексирование вообще предпочиталось с 1.
Неизвестный
12.12.2009, 13:58
общий
Иванов Андрей Владимирович:
Не стоит так отзываться об отвечающих. Вы задали вопрос не в ту рассылку и не указали версию языка (а они часто сильно отличаются). Платный вопрос - не средство обогащения эксперта и никаких особых обязанностей не налагает. Отвечают те, кому это интересно. Я тоже мог бы подать ответ.
Единственная ошибка в вашем первоначальном коде - сравнение последнего элемента с самим собой. Но и конечный код выглядит не слишком красиво. Блок "If i > 1 Then" бесполезен, висячий else в конце.
Если вы используете VB.Net, то вместо внутреннего цикла лучше воспользоваться сортирующей или хеширующей коллекцией. Т.к. цикл придает этой функции квадратную сложность и для больших массивов она будет очень неповоротливо работать.
Неизвестный
14.12.2009, 17:04
общий
Цитата: 302582
коды, которые предоставили на VB очень замудреные и не совсем работают. У меня у самого гораздо лучше получилось...

Я не стал бы так огульно охаивать. По крайней мере, мой код — это полная тестовая программа (точнее даже, две — на C++ и VB6). И обе работают — проверял. А функция заполнения массива — совсем небольшая.

Замечания по Вашему коду:
Код:

repeated = False
If i > 1 Then
For j = 1 To i - 1
If numbers(i) = numbers(j) Then
repeated = True
End If
Next
End If

1. условие If i > 1 Then излишне. Цикл FOR (по возрастанию переменной) не будет выполнен ни разу, если начальное значение больше конечного.
2. использовать в цикле numbers(i) — неэффективно. Лучше присвоить это значение простой переменной перед циклом. Именно поэтому у меня написано:
Код:

again:
tmp = Int((30000 * Rnd) + 1)
found = False
For j = 0 To i - 1
If nums(j) = tmp Then
found = True
Exit For
End If
Next j
If found Then GoTo again
nums(i) = tmp


GOTO не слишком красиво, конечно.
давно
Профессор
230118
3054
14.12.2009, 17:23
общий
amnick:
И мои работают. В Вашей программе непонятно одно - почему 30000, когда надо генерировать числа до 20?
Неизвестный
14.12.2009, 17:47
общий
Ashotn:
В задаче не сказано, что для массива из N чисел нужно генерировать числа от 1 до N. Это неявно прописано в коде вопрошающего (numbers(i) = Int(N * Rnd()) + 1), но, еще раз повторюсь, в условии задачи этого нет.

Вопрос был отправлен в раздел C/C++, а там функция rand() генерирует псевдослучайное число в диапазоне от 0 до RAND_MAX (=0x7fff = 32767), поэтому в варианте для VB6 я и задал 30000. По большому счету, это число взято с потолка — Rnd дает число с плавающей точкой, так что интервал [0.0, 1.0) можно отобразить в произвольный целочисленный.
Форма ответа