Выдать пять не повторяющихся карт

170
18 сентября 2019, 02:30

Условие задачи.
Дана колода из 52 карт. Необходимо выдать 5 не повторяющихся карт. Масти и номиналы хранить в двух разных массивах. Использовать класс string нельзя.

Что сделал я?

#include <iostream>
#include <ctime> // если старый компилятор, то заменить на "time.h"
// для отображения используемых в программе символов, измените шрифт консоли на "точечные шрифты"
int main()
{
    srand(time(0)); // для генерации псевдорандомных чисел с привязкой к текущему времени
    // объявляем и инициализируем константы
    const short jack = 11;
    const short queen = 12;
    const short king = 13;
    const short ace = 14;
    // используем только нужные объекты
    using std::cout;
    using std::endl;
    using std::cin;
    // создаем два массива для хранения мастей и номиналов
    char *suit = new char[4]{ 3, 4, 5, 6 }; // 3 ... 6 - коды в ascii для символов мастей
    short *face = new short[13]; // номиналы
    // заполняем массив номиналов цифрами от 2 до 14, 
    // где 2 ... 10 - будут выводится как простые числа, 
    // а 11 ... 14 - как символы, в соотвествии с константами
    for (int i = 0; i < 13; i++)
        face[i] = i + 2;
    cout << " Your Deck : " << endl;
    // случайные индексы элементов, которые мы будем выводить
    short rand_index_suit;
    short rand_index_face;
    for (int i = 1; i <= 5; i++) // выдаем 5 карт
    {
        // генерируем псевдорандомные числа (тут, индексы) и заполняем ими переменные
        rand_index_suit = rand() % 4; // от 0 до 3
        rand_index_face = rand() % 13; // от 0 до 12
        cout << " " << i << ". "; // выводим номер карты
        // если элемент с рандомным индексом равен следующему числу, то выводим его в соответствии с написанным ниже
        switch (face[rand_index_face]) 
        {
            case jack: { // валет
                cout << " J"; 
                break;
            }
            case queen: { // дама
                cout << " Q"; 
                break;
            }
            case king: { // король
                cout << " K";
                break;
            }
            case ace: { // туз
                cout << " A";
                break;
            }
            default: { // обычное число
                cout << " " << face[rand_index_face];
                break;
            }
        }
        // выводим масть с рандомным индексом
        cout << suit[rand_index_suit] << endl;
    }
    return 0; // если Visual Studio
}

Что не так с моей программой? Что нужно добавить?
В этом вся суть вопроса. Нужно выдать 5 не повторяющихся карт. А мое решение в худшем случае может выдать такие 5 карт:
K♠ K♠ K♠ K♠ K♠
Здесь я подразумеваю то, что карты могут повторяться в выводе моей программы.
Есть ли варианты решения этой проблемы, придуманные мной?
Откровенно говоря, за ними я и обратился сюда :).
Но один есть:
Выбрать начальную позицию (сгенерировать начальный индекс) в массиве номиналов и с каждым разом увеличивать его на один. Выход за границы массива предусмотрен.
5♠ 6♥ 7♥ 8♣ 9♠
Этот вариант не интересный и простой, как по мне.
Коротко о том, что не так и что нужно сделать
Мое решение может выдать 2 и больше одинаковых карт.
Примеры работы моей программы:
7♦ Q♠ A♣ 2♣ Q♠ - дважды повторилась карта Q♠;
2♦ 5♠ J♣ 2♦ 2♦ - трижды повторилась карта 2♦.
Нужно запретить программе делать такую ересь :).
Точнее говоря, нужно чтобы программа выдала 5 карт без повторов.
Напоминаю, использование класса string в решении этой задачи запрещено.

Надеюсь, я понятно описал свой вопрос. Заранее благодарен за любую вашу помощь!

Answer 1

Вот. Только, простите уж, десятку выдает как 0 :)

int main()
{
    int cards[52];
    for(int i = 0; i < 52; ++i) cards[i] = i;  // Заполнение колоды
    for(int i = 0; i < 5; ++i)   // Равномерное случайное перемешивание 
    {                            // первых 5 карт с выводом
        int j = rand()%(52-i)+i;
        int t = cards[j];
        cards[j] = cards[i];
        cards[i] = t;
        cout << "A234567890JQK"[t%13] << "♦♠♥♣"[t/13] << endl;
    }
}
Answer 2

Нужно где-то запоминать уже использованные карты.

#include <iostream>
#include <ctime> // если старый компилятор, то заменить на "time.h"
// для отображения используемых в программе символов, измените шрифт консоли на "точечные шрифты"
int main()
{
    srand(time(0)); // для генерации псевдорандомных чисел с привязкой к текущему времени
    // объявляем и инициализируем константы
    const short jack = 11;
    const short queen = 12;
    const short king = 13;
    const short ace = 14;
    // используем только нужные объекты
    using std::cout;
    using std::endl;
    using std::cin;
    // создаем два массива для хранения мастей и номиналов
    char *suit = new char[4]{ 3, 4, 5, 6 }; // 3 ... 6 - коды в ascii для символов мастей
    short *face = new short[13]; // номиналы
    // заполняем массив номиналов цифрами от 2 до 14, 
    // где 2 ... 10 - будут выводится как простые числа, 
    // а 11 ... 14 - как символы, в соотвествии с константами
    for (int i = 0; i < 13; i++)
        face[i] = i + 2;
    cout << " Your Deck : " << endl;
    // случайные индексы элементов, которые мы будем выводить
    short rand_index_suit;
    short rand_index_face;
    const short N=5;
    short deck_face[N]={};
    short deck_suit[N]={};
    for (int i = 0; i < N; i++) // выдаем 5 карт
    {
        bool used=true;
        // генерируем псевдорандомные числа (тут, индексы) и заполняем ими переменные
        while(used)
        {
            used = false;
            rand_index_suit = rand() % 4; // от 0 до 3
            rand_index_face = rand() % 13; // от 0 до 12
            for (int j = 0; j < i; j++)
            {
                if(deck_face[j]==face[rand_index_face] && deck_suit[j]==suit[rand_index_suit])
                {                   
                    used = true;
                    break;
                }
            }
            if(!used)
            {
                deck_face[i] = face[rand_index_face];
                deck_suit[i] = suit[rand_index_suit];
            }
        }
        cout << " " << i+1 << ". "; // выводим номер карты
        // если элемент с рандомным индексом равен следующему числу, то выводим его в соответствии с написанным ниже
        switch (face[rand_index_face]) 
        {
            case jack: { // валет
                cout << " J"; 
                break;
            }
            case queen: { // дама
                cout << " Q"; 
                break;
            }
            case king: { // король
                cout << " K";
                break;
            }
            case ace: { // туз
                cout << " A";
                break;
            }
            default: { // обычное число
                cout << " " << face[rand_index_face];
                break;
            }
        }
        // выводим масть с рандомным индексом
        cout << suit[rand_index_suit] << endl;
    }
    return 0; // если Visual Studio
}

Как пример, вместо 5 карт сгенерируем все 52 карты в случайном порядке. (изменил только N).

(не могу добавить комментарий так как <50 реп. Напишу тут)

@Harry у Вас, некорректно работает код. Как уже сделали в замечание, иногда могут попасться повторяющиеся карты.

Answer 3

Если количество выбираемых карт настолько сильно меньше размера множества, из которого делается выбор (5 из 52), то эффективнее всего применить "студенческий" алгоритм с запоминанием выбранных карт и повторным выбором

#include <iostream>
#include <cstdlib>
int main(int argc, const char * argv[]) 
{
  const char *const SUITS = "\x3\x4\x5\x6";
  const char *const FACES[] = { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" };
  unsigned char flags[4][13] = {};
  for (unsigned n = 5; n > 0; --n)
  {
    unsigned suit, face;
    do suit = std::rand() % 4, face = std::rand() % 13; while (flags[suit][face]);
    flags[suit][face] = true;
    std::cout << SUITS[suit] << FACES[face] << std::endl;
  }
}

Для задач, в которых количество выбираемых карт сравнимо с размером множества, такой алгоритм подходил бы плохо.

READ ALSO
C++ Создать экземпляр класса, используя заданный размер динамической памяти

C++ Создать экземпляр класса, используя заданный размер динамической памяти

Нужно создать экземпляр класса, используя заданный размер динамической памятиПробовал прописать вот так Stroka *stroka = new Stroka[10];, но появляется...

179
Насколько сейчас актуально WinAPI? [закрыт]

Насколько сейчас актуально WinAPI? [закрыт]

Изучаю разработку программ на C++ по книгамТам для создания окон и многого другого используется библиотека WinAPI

151
Присоеденение boost к CMake

Присоеденение boost к CMake

FIND_PACKAGE(Boost) - ищет boost по стандартному пути /usr/local/, мой boost лежит по пути /home/cheshirecat/libs Как сделать так, чтобы FIND_PACKAGE(Boost) нашел boost?

137