← Назад к вопросам

Генератор слайса уникальных чисел

1.6 Junior🔥 151 комментариев
#Основы Go

Условие

Напишите функцию, которая генерирует слайс из N уникальных случайных чисел в заданном диапазоне.

Сигнатура

func generateUniqueSlice(n, min, max int) ([]int, error)

Требования

  • Вернуть слайс из n уникальных чисел
  • Числа должны быть в диапазоне [min, max]
  • Если n > (max - min + 1), вернуть ошибку
  • Числа не должны повторяться

Пример

Вход: n = 5, min = 1, max = 10 Выход: []int{7, 2, 9, 1, 5} (или любая другая комбинация из 5 уникальных чисел)

Вход: n = 15, min = 1, max = 10 Выход: ошибка (невозможно выбрать 15 уникальных чисел из 10)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение

Анализ задачи

Требования:

  • Генерировать n уникальных случайных чисел в диапазоне [min, max]
  • Если невозможно выбрать n уникальных чисел, вернуть ошибку
  • Обрабатывать граничные случаи

Сложность: нужно избежать бесконечного цикла, если n больше количества доступных чисел.

Решение 1: Использование map для отслеживания

func generateUniqueSlice(n, min, max int) ([]int, error) {
    // Проверка валидности
    if n < 0 || min > max {
        return nil, errors.New("invalid parameters")
    }
    
    // Количество доступных чисел
    available := max - min + 1
    if n > available {
        return nil, fmt.Errorf("cannot generate %d unique numbers in range [%d, %d]", n, min, max)
    }
    
    // Используем map для отслеживания уже выбранных чисел
    used := make(map[int]bool)
    result := make([]int, 0, n)
    
    // Генерируем случайные числа
    for len(result) < n {
        randomNum := rand.Intn(max-min+1) + min
        if !used[randomNum] {
            used[randomNum] = true
            result = append(result, randomNum)
        }
    }
    
    return result, nil
}

Плюсы:

  • Простая логика
  • O(n) в среднем случае

Минусы:

  • При n близком к (max - min + 1) может быть медленным из-за большого количества коллизий

Решение 2: Метод перемешивания (Fisher-Yates)

func generateUniqueSlice(n, min, max int) ([]int, error) {
    // Проверка валидности
    if n < 0 || min > max {
        return nil, errors.New("invalid parameters")
    }
    
    available := max - min + 1
    if n > available {
        return nil, fmt.Errorf("cannot generate %d unique numbers in range [%d, %d]", n, min, max)
    }
    
    // Создаём слайс со всеми числами в диапазоне
    nums := make([]int, available)
    for i := 0; i < available; i++ {
        nums[i] = min + i
    }
    
    // Перемешиваем с помощью Fisher-Yates shuffle
    for i := len(nums) - 1; i > 0; i-- {
        j := rand.Intn(i + 1)
        nums[i], nums[j] = nums[j], nums[i]
    }
    
    // Возвращаем первые n элементов
    return nums[:n], nil
}

Плюсы:

  • O(max - min) один раз, затем O(n) для выбора
  • Гарантированно даст результат за одинаковое время
  • Все числа имеют равную вероятность

Минусы:

  • Требует больше памяти для промежуточного слайса

Решение 3: Оптимизированное (гибридное)

func generateUniqueSlice(n, min, max int) ([]int, error) {
    if n < 0 || min > max {
        return nil, errors.New("invalid parameters")
    }
    
    available := max - min + 1
    if n > available {
        return nil, fmt.Errorf("cannot generate %d unique numbers in range [%d, %d]", n, min, max)
    }
    
    // Если нужно генерировать больше половины диапазона, используем Fisher-Yates
    if n > available/2 {
        nums := make([]int, available)
        for i := 0; i < available; i++ {
            nums[i] = min + i
        }
        
        for i := len(nums) - 1; i > 0; i-- {
            j := rand.Intn(i + 1)
            nums[i], nums[j] = nums[j], nums[i]
        }
        
        return nums[:n], nil
    }
    
    // Иначе используем set
    used := make(map[int]bool)
    result := make([]int, 0, n)
    
    for len(result) < n {
        randomNum := rand.Intn(available) + min
        if !used[randomNum] {
            used[randomNum] = true
            result = append(result, randomNum)
        }
    }
    
    return result, nil
}

Полный пример с инициализацией seed

package main

import (
    "errors"
    "fmt"
    "math/rand"
    "time"
)

func generateUniqueSlice(n, min, max int) ([]int, error) {
    if n < 0 || min > max {
        return nil, errors.New("invalid parameters")
    }
    
    available := max - min + 1
    if n > available {
        return nil, fmt.Errorf("cannot generate %d unique numbers in range [%d, %d]", n, min, max)
    }
    
    if n > available/2 {
        nums := make([]int, available)
        for i := 0; i < available; i++ {
            nums[i] = min + i
        }
        
        for i := len(nums) - 1; i > 0; i-- {
            j := rand.Intn(i + 1)
            nums[i], nums[j] = nums[j], nums[i]
        }
        
        return nums[:n], nil
    }
    
    used := make(map[int]bool)
    result := make([]int, 0, n)
    
    for len(result) < n {
        randomNum := rand.Intn(available) + min
        if !used[randomNum] {
            used[randomNum] = true
            result = append(result, randomNum)
        }
    }
    
    return result, nil
}

func main() {
    rand.Seed(time.Now().UnixNano())
    
    nums, err := generateUniqueSlice(5, 1, 10)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Generated:", nums)
    }
    
    // Ошибка
    nums, err = generateUniqueSlice(15, 1, 10)
    if err != nil {
        fmt.Println("Error:", err)
    }
}

Рекомендация

Используй гибридный подход — он оптимален для всех случаев: быстрый для малых n и гарантированно завершающийся для больших n.