2010-07-20 149 views
1

我有一些随机字符串与未知内容,什么是已知的内容是字母数字和小写。从随机字母数字字符串的大写随机数字

我正在寻找一个简单的方法来大写随机数的字符串中的字母字符。随机性越高越好。

我可以想到一些方法来做到这一点,但没有一个看起来非常优秀。

好吗第一个解决方案:

public String randomizeCase(String myString){ 
    Random rand = new Random(); 
    StringBuilder build = new StringBuilder(); 
    for(char c: myString.toCharArray()){ 
    String s = new String(c); 
    if(Character.isLetter(c) && rand.nextBoolean()){ 
     s = s.toUpperCase(); 
    } 
    build.append(s); 
    } 
    return build.toString(); 
} 

我不喜欢这个解决方案,因为:

  • 50%的机会,每一个字符是大写不等于50%的机会字符的50%都大写
  • 有可能没有任何东西被加壳
  • 字符串转换很丑陋
+1

也许你能简要介绍一下你已经想出了自己的想法,然后大家一起讨论那些/给予提示? – 2010-07-20 18:12:07

+0

当你说“随机性越高越好。”你是在寻找接近真正的随机性的字母大写和/或哪些字母大写? – jball 2010-07-20 18:14:39

+0

添加了一些蹩脚的代码 – mkoryak 2010-07-20 18:28:53

回答

3

以下是随机样本问题(感谢Eyal命名它)的代码片段。不知道这是你在找什么。

请注意,如果字符串中没有足够的小写字母,则此解决方案将运行到infinete循环。所以你也需要解决这个问题,但我想这是一个起点。 ;-)

String myString = "9aie3ra3nr23rr5r21t"; 
System.out.println(upperCaseRandom(myString, 10)); 


public static String upperCaseRandom(String input, int n) { 
StringBuilder output = new StringBuilder(input); 
Random r = new Random(); 

for (int i = 0; i < n; i++) { 
    // Pick a place 
    int position = r.nextInt(input.length()); 

    // Check if lowercase alpha 
    if (Character.isLowerCase(output.charAt(position))) { 
    output.setCharAt(position, Character.toUpperCase(output.charAt(position))); 
    } else { 
    i--; 
    } 
} 
return output.toString(); 
} 

编辑: 这里是一个改进版本。它确切地将n个小写字母变成大写字母(如果有足够的,否则它会改变所有这些字母)。该程序不会陷入无限循环,但仍然运行时间是一个问题。

public static String upperCaseRandom(String input, int n) { 
    final int length = input.length(); 
    final StringBuilder output = new StringBuilder(input); 
    final boolean[] alreadyChecked = new boolean[length]; 
    final Random r = new Random(); 

    for (int i = 0, checks = 0; i < n && checks < length; i++) { 
     // Pick a place 
     int position = r.nextInt(length); 

     // Check if lowercase alpha 
     if (!alreadyChecked[position]) { 
      if (Character.isLowerCase(output.charAt(position))) { 
       output.setCharAt(position, Character.toUpperCase(output.charAt(position))); 
      } else { 
       i--; 
      } 
      checks++; 
      alreadyChecked[position] = true; 
     } else { 
      i--; 
     } 
    } 
    return output.toString(); 
} 
+0

这在所有情况下都无法正常工作(尝试它)。你可能会“切换”数字字符,导致没有效果。如果添加isLetter()条件,问题将得到解决,但由于试验和错误方法的无限性,此解决方案在大字符串(特别是多位数字)上的性能较差。 – 2010-07-20 19:24:31

+0

isLowerCase()将为数字字符返回false,因此它不会“切换”它们。我不能重新创建你的错误的情况,但也许我只是不明白到目前为止。你能重新检查和/或重新解释吗? 性能问题确实是一个问题,改进后的版本不应该再次检查相同的位置(只需添加一个布尔型[]来标识这些。而且仍然需要避免无限循环。 – 2010-07-20 20:25:30

+0

对不起,您是100%正确的代码的正确性,我的错误,它比我的最后一个解决方案还要短,唯一的缺点是性能,渐近的意义,为了转换所有的N个字符,预期的试验次数是N/N + N /(N-1 )+ ... + N/2 + N/1,其行为类似于N * log N.换句话说,最坏情况的复杂度是O(N log N)而不是O(N)。这是一个普遍的问题特定的随机样本算法。 – 2010-07-20 21:47:11

5

解决方案取决于您选择的概率模型。例如,如果您决定使用binomial distribution,则可以遍历字符,并以固定的概率p将每个字符切换为大写。大写字母的预计数量将是p * str.length():

public static String randomUpper(String str, double p) { 
    StringBuilder sb = new StringBuilder(str.length()); 
    for (int i = 0; i < str.length(); i++) { 
     char c = str.charAt(i); 
     if (Character.isLetter(c) && Math.random() < p) 
      c = Character.toUpperCase(c); 
     sb.append(c); 
    } 
    return sb.toString(); 
} 

如果你想对一个给定的字符串大写在前字母的确切数目决定另一方面,则问题变成了random sample problem (即,选择M个位置以切换出字符串中的N个位置)。这可以比第一种方法快得多,当M比N小得多时(尽管使用Java的不变字符串,差异变得很小,因为您必须复制整个字符串)。

- 编辑 -

现在你澄清的要求,考虑以下因素:

public static String randomUpper2(String str, double p) { 
    int letters = 0; 
    for (int i = 0; i < str.length(); i++) { 
     if (Character.isLetter(str.charAt(i))) 
      letters++; 
    } 

    int toChoose = (int) (p * letters); 
    StringBuilder sb = new StringBuilder(str.length()); 
    for (int i = 0; i < str.length(); i++) { 
     char c = str.charAt(i); 
     if (Character.isLetter(c)) { 
      if (Math.random() < (toChoose/(double)letters)) { 
       c = Character.toUpperCase(c); 
       toChoose--; 
      } 
      letters--; 
     }   
     sb.append(c); 
    } 
    return sb.toString(); 
} 

此代码“对飞”进行随机抽样,只考虑字母字符,按要求。使用p = 0.5切换正好一半的字母。

+0

+1。你还打我推荐'Character.toUpperCase()'。 – 2010-07-20 18:31:47

+0

当您从前到后遍历字符串并且Match.random()比较值变小(toChoose gets变小)时,这会导致后面的字符被更改的概率降低。因此它不是一个统一的分布。 – 2010-07-21 12:34:22

+0

@Nils:这可能看起来很明显,但这是一种已知的算法,它会在所有可能的子集上产生均匀的分布。查看我博客文章中的证明:http://eyalsch.wordpress.com/2010/04/01/random-sample/。请参阅“全面扫描”一节。 – 2010-07-21 12:51:11

0

我试着用

 String lowerCasedRandomString = "4210281f-76ac-96b5-ed54-5458abf788d0"; 
     String upperCasedRandomString = "4210281F-76AC-96B5-ED54-5458ABF788D0"; 
     System.out.println(lowerCasedRandomString.toUpperCase()); 
     System.out.println(upperCasedRandomString.toLowerCase()); 

我得到的输出

 4210281F-76AC-96B5-ED54-5458ABF788D0 
     4210281f-76ac-96b5-ed54-5458abf788d0