Что нового

_RandomEx - Функция для генерирования случайных чисел

XpycT

Скриптер
Сообщения
380
Репутация
132
Функция генерирует указанное кол-во случайных чисел и выводит их в указанном формате (в массиве или строке)

Код:
; #FUNCTION# ============================================================================================================
; Name...........: _RandomEx
; Description ...: Function generates a random numbers and displays them in a specified format.
; Syntax.........: _RandomEx($_iSNum, $_iENum, $_iRNumCount, $_iRetFormat, $_sRetDelimiter, $_iUnique)
; Parameters ....: $_iSNum         - The smallest number to be generated. The default is 0.
;                  $_iENum         - The largest number to be generated. The default is 1.
;                  $_iUnique       - Specified if generated random numbers should be unique (0 = not unique, 1 = unique (Default))
;                  $_iRNumCount    - Quantity of random numbers
;                  $_iRetFormat    - Return format (0 = String (Default), 1 = Array)
;                  $_sRetDelimiter - Random numbers delimeter if return format is string. The default is ","
; Return values .: Success - Return random numbers between $_iSNum and $_iENum in specified format.
;                  Failure - 0, sets @error  Returns 0 and sets @error flag to 1 if bad parameters
;                  |1 - $_iSNum equally or greater $_iENum
;                  |2 - Quantity of possible unique random numbers is less requested
; Author ........: XpycT
;                  Idea of return in string format madmasles http://autoit-script.ru/index.php/topic,6344.msg44489.html#msg44489
; =======================================================================================================================
Func _RandomEx($_iSNum = 0, $_iENum = 1, $_iUnique = 1, $_iRNumCount = 0, $_iRetFormat = 0, $_sRetDelimiter = ",")
    Local $sRNumStr = "`", $iNumCount = 0

    If $_iSNum >= $_iENum Then Return SetError(1, 0, 0)
    If $_iUnique And ($_iENum - $_iSNum + 1) < $_iRNumCount Then Return SetError(2, 0, 0)

    If $_iRNumCount = 0 Then $_iRNumCount = $_iENum - $_iSNum + 1

    While $iNumCount <> $_iRNumCount
        $iRNum = Random($_iSNum, $_iENum, 1)

        If $_iUnique = 1 Then
            If IsDeclared("<" & $iRNum & ">") Then ContinueLoop

            Assign("<" & $iRNum & ">", "", 1)

            $sRNumStr &= $iRNum & "`"

            $iNumCount += 1
        Else
            $sRNumStr &= $iRNum & "`"

            $iNumCount += 1
        EndIf
    WEnd
    $sRNumStr = StringTrimLeft(StringTrimRight($sRNumStr, 1), 1)

    If $_iRetFormat = 0 Then Return StringReplace($sRNumStr, "`", $_sRetDelimiter)
    If $_iRetFormat = 1 Then Return StringSplit($sRNumStr, "`")
EndFunc


Описание на русском
; #FUNCTION# ==========================================================================================================
; Название...............: _RandomEx
; Описание ..............: Функция генерирует случайные числа и выводит их в указанном формате.
; Синтаксис..............: _RandomEx($_iSNum, $_iENum, $_iRNumCount, $_iUnique, $_iRetFormat, $_sRetDelimiter)
; Параметры .............: $_iSNum - Наименьшее число, которые будет сгенерированно
; $_iENum - Наибольшее число, которые будет сгенерированно
; $_iUnique - Определяет должны ли быть случайные числа уникальными (0 = не уникальные, 1 = уникальные (По умолчанию))
; $_iRNumCount - Кол-во случайных чисел которое будет сгенерированно
; $_iRetFormat - Определяет в каком формате вернуть случайные числа (0 = Строка (По умолчанию), 1 = Массив)
; $_sRetDelimiter - разделитель случайных чисел, если возвращаемый формат строка. По умолчанию "," (запятая)
; Возвращаемое значение .: Success - Случайные числа в указанном формате
; Failure - 0, @error
; |1 - $_iSNum большо или равно $_iENum
; |2 - Кол-во возможных случайных чисел меньше чем указано
; Автор..................: XpycT
; Идея ввывода в формате строки madmasles http://autoit-script.ru/index.php/topic,6344.msg44489.html#msg44489
; =======================================================================================================================

Пример
Код:
; Return Unique Random Numbers in String Format
$sRandom = _RandomEx(1, 10, 1, 9)
ConsoleWrite("$sRandom = " & $sRandom & @CR)

; Return Random Numbers in Array Format
$aRandom = _RandomEx(1, 10, 0, 9, 1)
For $i = 1 To $aRandom[0]
    ConsoleWrite("$aRandom[" & $i & "] = " & $aRandom[$i] & @CR)
Next

; #FUNCTION# ============================================================================================================
; Name...........: _RandomEx
; Description ...: Function generates a random numbers and displays them in a specified format.
; Syntax.........: _RandomEx($_iSNum, $_iENum, $_iRNumCount, $_iRetFormat, $_sRetDelimiter, $_iUnique)
; Parameters ....: $_iSNum         - The smallest number to be generated. The default is 0.
;                  $_iENum         - The largest number to be generated. The default is 1.
;                  $_iUnique       - Specified if generated random numbers should be unique (0 = not unique, 1 = unique (Default))
;                  $_iRNumCount    - Quantity of random numbers
;                  $_iRetFormat    - Return format (0 = String (Default), 1 = Array)
;                  $_sRetDelimiter - Random numbers delimeter if return format is string. The default is ","
; Return values .: Success - Return random numbers between $_iSNum and $_iENum in specified format.
;                  Failure - 0, sets @error  Returns 0 and sets @error flag to 1 if bad parameters
;                  |1 - $_iSNum equally or greater $_iENum
;                  |2 - Quantity of possible unique random numbers is less requested
; Author ........: XpycT
;                  Idea of return in string format madmasles http://autoit-script.ru/index.php/topic,6344.msg44489.html#msg44489
; =======================================================================================================================
Func _RandomEx($_iSNum = 0, $_iENum = 1, $_iUnique = 1, $_iRNumCount = 0, $_iRetFormat = 0, $_sRetDelimiter = ",")
    Local $sRNumStr = "`", $iNumCount = 0

    If $_iSNum >= $_iENum Then Return SetError(1, 0, 0)
    If $_iUnique And ($_iENum - $_iSNum + 1) < $_iRNumCount Then Return SetError(2, 0, 0)

    If $_iRNumCount = 0 Then $_iRNumCount = $_iENum - $_iSNum + 1

    While $iNumCount <> $_iRNumCount
        $iRNum = Random($_iSNum, $_iENum, 1)

        If $_iUnique = 1 Then
            If IsDeclared("<" & $iRNum & ">") Then ContinueLoop

            Assign("<" & $iRNum & ">", "", 1)

            $sRNumStr &= $iRNum & "`"

            $iNumCount += 1
        Else
            $sRNumStr &= $iRNum & "`"

            $iNumCount += 1
        EndIf
    WEnd
    $sRNumStr = StringTrimLeft(StringTrimRight($sRNumStr, 1), 1)

    If $_iRetFormat = 0 Then Return StringReplace($sRNumStr, "`", $_sRetDelimiter)
    If $_iRetFormat = 1 Then Return StringSplit($sRNumStr, "`")
EndFunc

Пример показывающий время генерации чисел в старой и новой версии
[box title=На моем ноутбуке время генерации чисел такое]
$iTimer_RandomEx_v1 = 145673.949342724
$iTimer_RandomEx_v2 = 822.24696155517
[/box]
Код:
Dim $iMin = 1, $iMax = 10000, $iNum = 9999, $iUnique = 1, $iFormat = 0

$iTimer_RandomEx_v1 = TimerInit()
$sRandom = _RandomEx_v1($iMin, $iMax, $iUnique, $iNum, $iFormat, " | ")
ConsoleWrite("$iTimer_RandomEx_v1 = " & TimerDiff($iTimer_RandomEx_v1) & @CR)

$iTimer_RandomEx_v2 = TimerInit()
$sRandom = _RandomEx($iMin, $iMax, $iUnique, $iNum, $iFormat, " | ")
ConsoleWrite("$iTimer_RandomEx_v2 = " & TimerDiff($iTimer_RandomEx_v2) & @CR)

Func _RandomEx($_iSNum = 0, $_iENum = 1, $_iUnique = 1, $_iRNumCount = 0, $_iRetFormat = 0, $_sRetDelimiter = ",")
    Local $sRNumStr = "`", $iNumCount = 0

    If $_iSNum >= $_iENum Then Return SetError(1, 0, 0)
    If $_iUnique And ($_iENum - $_iSNum + 1) < $_iRNumCount Then Return SetError(2, 0, 0)

    If $_iRNumCount = 0 Then $_iRNumCount = $_iENum - $_iSNum + 1

    While $iNumCount <> $_iRNumCount
        $iRNum = Random($_iSNum, $_iENum, 1)

        If $_iUnique = 1 Then
            If IsDeclared("<" & $iRNum & ">") Then ContinueLoop

            Assign("<" & $iRNum & ">", "", 1)

            $sRNumStr &= $iRNum & "`"

            $iNumCount += 1
        Else
            $sRNumStr &= $iRNum & "`"

            $iNumCount += 1
        EndIf
    WEnd
    $sRNumStr = StringTrimLeft(StringTrimRight($sRNumStr, 1), 1)

    If $_iRetFormat = 0 Then Return StringReplace($sRNumStr, "`", $_sRetDelimiter)
    If $_iRetFormat = 1 Then Return StringSplit($sRNumStr, "`")
EndFunc

Func _RandomEx_v1($_iSNum = 0, $_iENum = 1, $_iUnique = 1, $_iRNumCount = 1, $_iRetFormat = 0, $_sRetDelimiter = ",")
    Local $sRNumStr = "`", $iNumCount = 0

    If $_iSNum >= $_iENum Then Return SetError(1, 0, 0)
    If $_iUnique And ($_iENum - $_iSNum + 1) < $_iRNumCount Then Return SetError(2, 0, 0)

    If $_iRNumCount = 0 Then $_iRNumCount = $_iENum - $_iSNum + 1

    While $iNumCount <> $_iRNumCount
        $iRNum = Random($_iSNum, $_iENum, 1)

        If $_iUnique = 1 Then
            If Not StringInStr($sRNumStr, "`" & $iRNum & "`") Then
                $sRNumStr &= $iRNum & "`"

                $iNumCount += 1
            EndIf
        Else
            $sRNumStr &= $iRNum & "`"

            $iNumCount += 1
        EndIf
    WEnd
    $sRNumStr = StringTrimLeft(StringTrimRight($sRNumStr, 1), 1)

    If $_iRetFormat = 0 Then Return StringReplace($sRNumStr, "`", $_sRetDelimiter)
    If $_iRetFormat = 1 Then Return StringSplit($sRNumStr, "`")
EndFunc

История Весий
v2 [08.01.2012] - Изменен способ проверки уникальности.
AZJIO [?]
за счёт поиска повторов не в строке, а методом создания локальных переменных

v1 [28.08.2011] - Первый выпуск.

Автор(ы): XpycT (функция), идея ввывода в формате строки madmasles,

P.S.
С первой "полезняшкой" меня :laugh: :laugh: :laugh:
 

Zaramot

I ♥ AutoIt
Сообщения
1,160
Репутация
660
Зачем было писать описание функции на английском ?

P.S Сдаётся мне что это не твоя полезняшка :scratch:
 

Zaramot

I ♥ AutoIt
Сообщения
1,160
Репутация
660
XpycT не вкоем случаи не хотел вас обидеть, но не лучше перевести на русский, так как думаю не все знают хорошо английский. В частности Я ;D
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,717
Хорошая функция, но почему при $_iUnique = 0 она вываливается с ошибкой 3? И еще я бы поменял заголовок:

Код:
_RandomEx($_iSNum, $_iENum, $_iRNumCount, $_iUnique = 1, $_iRetFormat = 0, $_sRetDelimiter = ",")


т.к. $_iUnique, IMHO, более востребовательный параметр.
 
Автор
X

XpycT

Скриптер
Сообщения
380
Репутация
132
Yashied [?]
$_iUnique = 0 она вываливается с ошибкой 3
Странно :scratch: не должна вываливаться, ошибра 3 обозначает что кол-во возможных чисел меньше чем указанно в параметре $_iRNumCount

$_iUnique, IMHO, более востребовательный параметр.
Согласен. Щас поменяю заголовок
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,717
Код:
$aRandomNumArray = _RandomEx(1, 10, 9, 0, "", 0)
ConsoleWrite(@error & @CR)
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
964
Yashied [?]
Хорошая функция, но почему при $_iUnique = 0 она вываливается с ошибкой 3?
$_iUnique IMHO тут не причём, тут работает условие..
Код:
If ($_iENum - $_iSNum - 1) < $_iRNumCount Then Return SetError(3, 0, 0)
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,717
Garrett сказал(а):
$_iUnique IMHO тут не причём, тут работает условие...

Так быть не должно при $_iUnique = 0. Если массив не уникальный, то какая разница, какой диапазон использовать, хоть от 0 до 1.
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
964
Yashied
Я, по-моему, чего-то не улавливаю, но в вашем примере _RandomEx(1, 10, 9... при любом значение $_iUnique будет вываливаться третья ошибка.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
XpycT,
А почему нельзя использовать отрицательные числа? ИМХО, вполне нормальное выражение:
Код:
ConsoleWrite(Random(-10, -5, 1) & @LF)


Это условие не правильное:
Код:
If ($_iENum - $_iSNum - 1) < $_iRNumCount Then Return SetError(3, 0, 0)
Нормально работает:
Код:
$iCount = 0
$_sRetDelimiter = ','
$sString = $_sRetDelimiter

$_iSNum = 1
$_iENum = 20
$_iRNumCount = 20
ConsoleWrite('$_iENum - $_iSNum = ' & $_iENum - $_iSNum & @LF)
ConsoleWrite('$_iRNumCount = ' & $_iRNumCount & @LF)
While $iCount < $_iRNumCount
	$iRandom = Random($_iSNum, $_iENum, 1)
	If Not StringInStr($sString, $_sRetDelimiter & $iRandom & $_sRetDelimiter) Then
		$sString &= $iRandom & $_sRetDelimiter
		$iCount += 1
	EndIf
WEnd
ConsoleWrite(StringTrimLeft(StringTrimRight($sString, 1), 1) & @LF)


Можно сразу заданный разделитель использовать и убрать лишний
Код:
StringReplace($sRNumStr, "`", $_sRetDelimiter)
 

focus

Осваивающий
Сообщения
69
Репутация
20
Запустил вышеуказаный пример и
Код:
$sRandom = 5,6,8,3,1,4,2,7,9
C:\Program Files\AutoIt3\SKRIPT\????\113.au3 (7) : ==> Subscript used with non-Array variable.:
For $i = 1 To $aRandom[0]
For $i = 1 To $aRandom^ ERROR
 
Автор
X

XpycT

Скриптер
Сообщения
380
Репутация
132
madmasles [?]
А почему нельзя использовать отрицательные числа?
Сделаем и с отрицательными числами
Уже можно

ИМХО, это условие не правильное
Согласен не правильно. Вроде исправил, должно нормально генерировать

Можно сразу заданный разделитель использовать
Спасибо за подсказку, как то не додумался


Как водиться первый блин комом ;D


Добавлено:
Сообщение автоматически объединено:

focus
В примере ошибка была, попробуй еще раз
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
XpycT,
Вроде все нормально стало. :beer:
 
Автор
X

XpycT

Скриптер
Сообщения
380
Репутация
132
madmasles [?]
Можно сразу заданный разделитель использовать
А вот и нельзя, потому что можно указать $_sRetDelimiter = "" и тогда сбивается условие проверки на уникальность.
 

focus

Осваивающий
Сообщения
69
Репутация
20
Да, теперь всё нормально.
OffTopic:
( С почином ! )
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
XpycT [?]
А вот и нельзя, потому что можно указать $_sRetDelimiter = "" и тогда сбивается условие проверки на уникальность.
Мне трудно понять, какой смысл такой строки:
Код:
$sRandom = _RandomEx(1, 100, 100, 1, 0, '')
Я бы добавил:
Код:
;...
Local $i_FlagSplit, $i_Len_Delimiter
If StringRegExp($_sRetDelimiter, '[0-9]') Then $_sRetDelimiter = ','
If Not $_sRetDelimiter Then $_sRetDelimiter = ','
$i_Len_Delimiter = StringLen($_sRetDelimiter)
If $i_Len_Delimiter > 1 Then $i_FlagSplit = 1
;...
$sRNumStr = StringTrimLeft(StringTrimRight($sRNumStr, $i_Len_Delimiter), $i_Len_Delimiter)
;...
 If $_iRetFormat = 1 Then Return StringSplit($sRNumStr, $_sRetDelimiter, $i_FlagSplit)
Если могут указать разделитель $_sRetDelimiter = '', то могут и $_sRetDelimiter = '33', и $_sRetDelimiter = ',zz;', и т.д. и т.п. :smile:
 
Автор
X

XpycT

Скриптер
Сообщения
380
Репутация
132
madmasles [?]
Мне трудно понять, какой смысл такой строки
Если честно то мне тоже.

OffTopic:
Ладно время позднее, я :sleeping:, завтра обновлю функцию
 

AZJIO

Меценат
Меценат
Сообщения
2,765
Репутация
1,154
Для большинства случаев подходит этот вариант, но иногда нужна возможность перебирать рандом в условиях возврата 9999 элементов из 10000 допустимых рандомных. Тут проще инвертировать задачу - вызвать один рандом и использовать как исключение, но этот вариант ещё реализовать нужно с учётом гибкости. Текущий UDF на с такими параметрами надолго зависает, так как идёт постоянный повтор тех же случайностей. Для решения задачи нужно сделать массив элементов и исключать вызванные рандомы, дабы постоянно сужать круг доступных значений до 0, при этом скрипт подходит к пределу 100% завершения задачи, в то время как текущий UDF идёт к пределу наименьшей вероятности решения задачи.
На офсайте _RandomUnique аналог, но работает намного быстрее за счёт поиска повторов не в строке, а методом создания локальных переменных. Но и этот метод на грани "99999 элементов из 100000" начинает уже тормозить до 6 сек. Частично побороть можно методом, когда функция начинает часто делать ложные (уже выпавшие) рандомы (четверть от количества элементов) нужно сузить круг поиска, то есть убрать из массива отработанные элементы, при этом на порядок уменьшается их количество.

Вот текущая проба:
Код:
#include <Array.au3>

$timer = TimerInit()
$aR = _RandomUnique(0, 100000, 99999)
_ArrayDisplay($aR, 'Time : ' & Round(TimerDiff($timer) / 1000, 2) & ' sec', 100)

Func _RandomUnique($Min = 0, $Max = 1, $Count = 1)
	Local $i, $k, $ts, $te, $tmp, $tmpNew, $iStep = 0, $nRnd, $iStepErr = 0

	$k = $Max - $Min + 1
	If $Min >= $Max Then Return SetError(1, 0, '')
	If $k < $Count Then Return SetError(2, 0, '')
	If $Count = 1 Then
		Local $aUnique[2] = [1, Random($Min, $Max, 1)]
		Return $aUnique
	EndIf

	Local $aStr[$k]
	For $i = 0 To $k - 1
		$aStr[$i] = $i + $Min
	Next

	If $Count > $k / 2 Then

		Local $iCountExp = $k - $Count
		Assign('\', '', 1)
		While $iStep < $iCountExp
			$nRnd = Random(0, $k - 1, 1)
			If IsDeclared($aStr[$nRnd] & '\') <> -1 Then
				$iStep += 1
				$aStr[$nRnd] = ''
				Assign($aStr[$nRnd] & '\', '', 1)
			Else
				; v===================================v
				$iStepErr += 1
				If $iStepErr > $k / 4 Then
					$d = 0
					For $i = 0 To $k - 1
						If $aStr[$i] Then
							$aStr[$d] = $aStr[$i]
							$d += 1
						EndIf
					Next
					If $d > 0 Then
						ReDim $aStr[$d]
						$k = $d
						$iStepErr = 0
					Else
						ExitLoop
					EndIf
				EndIf
				; ^===================================^
			EndIf
		WEnd
		$d = 0
		Local $aUnique[$k]
		For $i = 0 To $k - 1
			If StringLen($aStr[$i]) Then
				$d += 1
				$aUnique[$d] = $aStr[$i]
			EndIf
		Next
		ReDim $aUnique[$d + 1]
		$aUnique[0] = $d

		; swap
		For $i = 1 To $aUnique[0]
			$iR = Random($i, $aUnique[0], 1)
			If $i = $iR Then ContinueLoop
			If $iR = 0 Then $iR = $aUnique[0]
			$aTmp = $aUnique[$i]
			$aUnique[$i] = $aUnique[$iR]
			$aUnique[$iR] = $aTmp
		Next

	Else

		Local $aUnique[$Count + 1] = [$Count]
		Assign('\', '', 1)
		While $iStep < $Count
			$nRnd = Random(0, $k - 1, 1)
			If IsDeclared($aStr[$nRnd] & '\') <> -1 Then
				$iStep += 1
				$aUnique[$iStep] = $aStr[$nRnd]
				$aStr[$nRnd] = ''
				Assign($aStr[$nRnd] & '\', '', 1)
			Else
				; v===================================v
				$iStepErr += 1
				If $iStepErr > $k / 4 Then
					$d = 0
					For $i = 0 To $k - 1
						If StringLen($aStr[$i]) Then
							$aStr[$d] = $aStr[$i]
							$d += 1
						EndIf
					Next
					If $d > 0 Then
						ReDim $aStr[$d]
						$k = $d
						$iStepErr = 0
					Else
						ExitLoop
					EndIf
				EndIf
				; ^===================================^
			EndIf
		WEnd
	EndIf

	Return $aUnique
EndFunc   ;==>_RandomUnique
 
Верх