提高Objective-C中随机性的质量

时间:2011-10-12 02:57:33

标签: objective-c ios random

你站在地牢里。在你之前,有一组5级书呆子。他们希望你为他们开展“龙与地下城”活动。

你开了几场比赛,你的球员正在升级,而且事情通常会膨胀。但是战斗有点慢。你决定拿出你的+4 Halberd of Objective-C并编写一个iPad应用来自动化NPC骰子在战斗中滚动。 威胁说,书呆子向你走来。 “算法生成的数字,”一个人咆哮道,“是对真正随机性的空洞模仿!你不应该用你的伪随机污秽来玷污我们的圣战!”

你滚动说服他arc4random_uniform()不仅仅是足够......而且失败了。书呆子会满足于真正的随机性。当你拼命地依赖你的MacBook时,他们会把你当作囚犯,写一个从random.org中检索数据的课程。

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"YYYY/YYYY-MM-dd"];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@%@", 
                @"http://www.random.org/files/", 
                [formatter stringFromDate:[NSDate date]],
                @".bin"]];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:
                [NSURLRequest requestWithURL:url] delegate:self];

保存数据后,您可以从下载的字节中生成随机数0-255。

-(int) nextInt:(int)start end:(int)end
{
    int upperBound = end - start + 1;
    unsigned char result;
    int maxModulo = 255 - (255 % upperBound);
    do {
        NSRange range = {index, sizeof(char)};
        [randos getBytes:&result range:range];
        index += sizeof(char);
    } while (result > maxModulo); //avoid modulo bias
    result = result % upperBound;
    result += start;
    return result;
}

书呆子似乎很满意,但是出现了一个新的敌人:另一个地牢大师!他要求你出于自己的目的给他一份软件副本。问题很明显 - 如果你们都在同一天使用random.org数据,你将获得相同的骰子卷!

所以我的问题如下:我如何修改random.org数据,使其保留类似“真正随机性”的内容,但在程序的每个实例中都有所不同?我可以想象一个解决方案,需要从用户like TrueCrypt does获得一些(据称是随机的)触摸板动作,但是一旦我有这个常数,我不确定从那里去哪里。以某种方式使用我的常量哈希所有数字?这将产生更大的数字;我在统计上是好的,如果我只是截断或模数到骰子卷?我不知道采取什么算法步骤。

5 个答案:

答案 0 :(得分:5)

我有一个不同的解决方案。一个能满足每个人的,我希望。每次要生成新的掷骰子时,都要这样做:

显示进度条,并提示用户摇动设备。

  • 在等待时,适当地过滤加速度数据(一些低通IIR应该很好)并寻找具有一定给定幅度的尖峰。使用滞后计数级别更改。显示一个显示摇晃量的进度条。
  • 同时,将原始加速度数据提供给合适的加密哈希函数,例如SHA-2。

当进度条一直向右移动时,播放掷骰子的声音并使用散列函数的输出(256位)生成骰子值。这不会超过59d20。您还可以将哈希函数状态保存为下一个掷骰子的输入。

这就是你告诉那些书呆子的事情:模具卷绝不是算法上可预测的。确定模具卷的唯一信息是你如何摇动设备,这对于真正的骰子也是如此。在理论中你可以用同样的方式摇动设备两次,就像在理论中一个高技能的赌徒可以掷出真正的骰子,让它们以他想要的方式出现。

如何使用输出:你有256位的随机数据,并希望得到掷骰。

struct bits {
    unsigned data[8];
    unsigned pos;
};

// Get next n bits, or -1 if out of entropy.
int get_bits(struct bits *b, int n);

// Roll an n-sided die, or -1 if out of entropy
int uniform(struct bits *b, int n)
{
    int nbits, x;
    for (nbits = 0; (1 << nbits) < n; ++nbits);
    do {
        x = get_bits(b, nbits);
        if (x < 0)
            return -1;
    } while (x >= n);
    return x + 1;
}

此功能的工作原理是一次切割几个熵的骰子。因此对于d8,您需要切3位并使用结果。对于d20,你为d32切掉5位,如果结果大于20则重新滚动。如果你碰巧用完给定的掷骰子的熵(不太可能,但可能),那么我建议打印一个“骰子”高举“消息并要求用户为剩下的骰子摇一摇。

  

脚注:除非你滚动大量的骰子,否则你的熵耗尽的概率非常低。 256位就足够了。在熵耗尽之前需要24d20甚至达到1%。

答案 1 :(得分:4)

这不是一个真正的答案而且是一个评论,但它变得很长而且就在这里。

  

我可以想象一个解决方案,需要从用户那里获得一些(据称是随机的)触摸板动作,

请注意,arc4random_stir()/dev/urandom读取,请参阅the man page。所以它以“环境”为自己种子。

或者,购买放射源和盖革计数器,将其连接到USB,并根据计数器的读数生成随机数。核衰变是量子力学随机的。

答案 2 :(得分:2)

生成加密安全随机字节数组。

int SecRandomCopyBytes (
   SecRandomRef rnd,
   size_t count,
   uint8_t *bytes
);

Apple Randomization Services Reference Docs

或者只是使用arc4random(),它尽可能接近随机,它会自动从/ ​​dev / urandom播种。

答案 3 :(得分:1)

下载bin-files.txt并随机选择其中一个条目(例如,使用NSDate timeIntervalSinceNow modulo txt文件中的条目数)。然后下载该文件。

此外,下载文件后,根据不同的随机数发生器开始一些偏移。

答案 4 :(得分:0)

无论使用哪种随机方法(我选择了_arc4random_uniform_),在某些时候仍然需要骰子计算器。

从方法主体中推断出标题。这是我的用途:

objective-c
//
//  Dice.m
//  TheeEndThee
//
//  Created by Martin-Gilles Lavoie on 2019-10-19.
//  Copyright © 2019 DoRyu. All rights reserved.
//

#import "Dice.h"

@implementation Dice

+ (NSInteger) roll: (NSUInteger) sides
             times: (NSUInteger) numDices
              plus: (NSInteger) bonus
{
    NSInteger   result  =   bonus;

    while (numDices-- > 0)
    {
        result += (arc4random() % sides) + 1;
    }

    return result;
}

+ (NSInteger) roll100   {   return [self roll: 100 times: 1 plus: 0];   }
+ (NSInteger) roll30    {   return [self roll: 30 times: 1 plus: 0];    }
+ (NSInteger) roll24    {   return [self roll: 24 times: 1 plus: 0];    }
+ (NSInteger) roll20    {   return [self roll: 20 times: 1 plus: 0];    }
+ (NSInteger) roll12    {   return [self roll: 12 times: 1 plus: 0];    }
+ (NSInteger) roll10    {   return [self roll: 10 times: 1 plus: 0];    }
+ (NSInteger) roll8     {   return [self roll: 8 times: 1 plus: 0];     }
+ (NSInteger) roll6     {   return [self roll: 6 times: 1 plus: 0];     }
+ (NSInteger) roll4     {   return [self roll: 4 times: 1 plus: 0];     }
+ (NSInteger) roll2     {   return [self roll: 2 times: 1 plus: 0];     }

+ (NSInteger) rollStat  {   return [self roll: 6 times: 3 plus: 0];     }

+ (NSObject*) compute: (NSObject*) value
{
    NSObject*   result  =   value;

    if ([value isKindOfClass: NSString.class])
    {
        NSString*   expression  =   (id) value;

        //whitespaces and newlines are nod valid dice expressions
        if ([expression rangeOfCharacterFromSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]].location == NSNotFound
        && [expression hasPrefix: @"="])
        {
            expression = [expression substringFromIndex: 1];

            NSInteger   multiplier  =   1;
            NSInteger   addition    =   0;
            NSInteger   faces;

            NSArray<NSString*>*     components  =   [expression componentsSeparatedByString: @"+"];

            if (components.count == 2)
            {
                addition = components.lastObject.integerValue;

                expression = components.firstObject;
            }

            expression  =   expression.lowercaseString;

            components  =   [expression componentsSeparatedByString: @"d"];

            if (components.count == 2)
            {
                multiplier = components.firstObject.integerValue;

                expression = components.lastObject;
            }

            faces = expression.integerValue;

            if (faces > 0 && multiplier > 0)
            {
                result = @([self roll: faces
                                times: multiplier
                                 plus: addition]);

#if DEBUG
                NSLog(@"Dice \"%@\" = %@d%@+%@ = %@",
                      value, @(multiplier), @(faces), @(addition), result);
#endif
            }
            else
            {
                result = nil;
            }
        }
    }

    return result;
}

@end

[骰子计算:表达式] 将解析以下性质的表达式:

=6.      // expression to roll a 6-sided dice
=1d6     // expression to roll a 6-sided dice
=6+2     // expression to roll a 6-sided dice and add 2
=1d6+2   // expression to roll a 6-sided dice and add 2

{multiplier} {d} faces {+ bonus}语法可以产生给定面孔的任何编号的骰子,最后带有奖励。

我的程序使用变量替换操作来处理“ 1d20 + MOD”之类的问题。在自己的程序中发疯。

相关问题