#java #algorithm #math #probability
#java #алгоритм #математика #вероятность
Вопрос:
У меня есть книга с N <10000 страниц и числом x (в диапазоне 1 <= x <= 40). Я хочу вычислить вероятность того, что при случайном открытии этой книги комбинация цифр открытых страниц книги будет равна числу.
«Уровень комбинаций» может варьироваться: от простой суммы цифр (событие стр.234 верно для x = 9) до комбинации сумм и вычитаний вплоть до пар цифр [событие стр.124 верно для x = 1, 2, 3(4-1), 4, 5(4 1), 6(2 4), 7(1 2 4), 8(12-4), 12, 14, 16(14 2), 23(24-1), 24, 25(24 1) ]
Начальное замечание заключается в том, что если вы откроете книгу, вы всегда получите страницу n и страницу n 1, поэтому вероятность должна быть рассчитана для пары (2n-1,2n), для каждого n, 1
Вот что я делаю
static protected int sommaCifreNumero(int numero){
int retnum=0;
for (char c : Integer.valueOf(numero).toString().toCharArray()){
retnum = c - 48;
}
return retnum;
}
static public float calcolaProbabilitàSemplice(int da_interrogare, int ne_interroga)
{
return (float)ne_interroga/(float)da_interrogare*100f;
}
/*
* Questo sistema calcola le probabilità che aprendo un libro a caso,
* la somma delle cifre delle pagine diano il tuo numero nell'elenco del registro.
* Se il tuo numero non può essere raggiunto, avrai sempre probabilità 0%.
*/
static public float calcolaProbabilitàLibroSemplice(int nPagine, int nRegistro)
{
int maxNumberInterrogabile = 0;
float retProb;
maxNumberInterrogabile = sommaCifreNumero (nPagine);
maxNumberInterrogabile = ((Integer.valueOf(nPagine).toString().length() == 2) amp;amp; (Integer.valueOf(nPagine).toString().toCharArray()[1] -48 -1 9*1)>maxNumberInterrogabile) ? (Integer.valueOf(nPagine).toString().toCharArray()[1] -48 -1 9*1) : maxNumberInterrogabile;
maxNumberInterrogabile = ((Integer.valueOf(nPagine).toString().length() == 3) amp;amp; (Integer.valueOf(nPagine).toString().toCharArray()[2] -48 -1 9*2)>maxNumberInterrogabile) ? (Integer.valueOf(nPagine).toString().toCharArray()[1] -48 -1 9*2) : maxNumberInterrogabile;
maxNumberInterrogabile = ((Integer.valueOf(nPagine).toString().length() == 4) amp;amp; (Integer.valueOf(nPagine).toString().toCharArray()[3] -48 -1 9*3)>maxNumberInterrogabile) ? (Integer.valueOf(nPagine).toString().toCharArray()[1] -48 -1 9*3) : maxNumberInterrogabile;
if(nRegistro>maxNumberInterrogabile)
{
retProb = 0.f;
return 0.f;
}//il numero massimo raggiungibile è inferiore al numero in registro -> non puoi essere chiamato
int favorevoli = 0;
for(int i=1; i<=nPagine; i )
{
if(sommaCifreNumero(i)==nRegistro || i==nRegistro)
favorevoli ;
}
retProb = (float) favorevoli / (float) nPagine * 100f;
return retProb;
}
/*
* Questo sistema è un'estensione del precedente: somma le cifre
* di una pagina aperta a caso, ma anche a coppie(es: p.124 può dare 12, 16, 24, 25).
*/
static public float calcolaProbabilitàLibroComplessa(int nPagine, int nRegistro)
{
String pagstring;
float retProb;
int nRegLength = String.valueOf(nRegistro).length();
int favorevoli = 0;
int totali = 0;
Vector<Integer> possibili;
int number_to_add;
int number_added;
for(int i = 1;i<=nPagine; i )
{
possibili = new Vector<Integer>();
pagstring = Integer.valueOf(i).toString();
for(int a=0; a nRegLength<=pagstring.length(); a )
{
String numero_selezionato = pagstring.substring(a,a nRegLength);
if (Integer.parseInt(numero_selezionato)<=31) possibili.add(Integer.parseInt(numero_selezionato));
//somma le parti prima
for(int b=0; b<a; b )
{//b è l'indice iniziale della sottostringa che verrà sommata
for(int c=1; c<=nRegLength; c )
{//c è l'indice 1 finale della sottostringa che verrà sommata
if(b c<=a)
{
number_to_add = Integer.parseInt(pagstring.substring(b,b c));
if (number_to_add!=0)
{
number_added = Integer.parseInt(numero_selezionato) number_to_add;
if (number_added <31) possibili.add(number_added);
}
}
}
}
//somma le parti dopo
for(int b=a nRegLength; b<pagstring.length(); b )
{
for(int c=1; c<=nRegLength; c )
{
if(b c<=pagstring.length())
{
number_to_add = Integer.parseInt(pagstring.substring(b,b c));
if (number_to_add!=0)
{
number_added = Integer.parseInt(numero_selezionato) number_to_add;
if (number_added <31) possibili.add(number_added);
}
}
}
}
totali = possibili.size();
for(int numero: possibili) favorevoli = numero==nRegistro ? 1:0;
}
}
retProb = (float)favorevoli/(float)totali * 100f;
return retProb;
}
Первый метод вычисляет сумму цифр числа, второй — вероятность того, что номер открытой страницы равен x, или сумма их цифр равна.
Третья проверка также пары цифр.
1) Я не учитываю заметку, которую я сделал раньше.
2) Я запущу это на мобильном устройстве.
3) Прямо сейчас я действительно чувствую, что результаты неверны.
Мне было интересно, подойдет ли таблица предварительно рассчитанных результатов лучше. Я знаю, что N равно <10000, поэтому я могу использовать массив [40] [10000] для хранения результатов для загрузки во время выполнения, но я не увлекаюсь манипуляциями с файлами в Java, кроме того, мне нужно будет сохранить это, скажем, для 4 разных методов вычисления вероятностиитак, сколько памяти это будет потреблять? И является ли проблемой вычислить это во время выполнения вместо этого? Есть ли лучший подход (или, может быть, уже написанный алгоритм) для этого?
Комментарии:
1. Я не думаю, что смогу вам сильно помочь с алгоритмом, тем более, что я нахожу ваш код действительно трудным для чтения (языковой барьер), но если вы готовы предварительно рассчитать его и прочитать из файла, это было бы действительно легко, и есть много способов. Возможно, проще всего было бы поместить их в форму x.y=… и прочитать его как файл свойств. (ResourceBundle.getBundle(filenameMinusExtension).getString(«x.y»)). Хотя это, скорее всего, загрузит весь файл в память при запуске.
2. @Makers_F: 1, забавная проблема. Вопрос о требованиях: каково максимальное значение N?
3. @user988052 Значение N может быть установлено равным 9999, но я вряд ли думаю, что оно будет установлено больше 400 когда-либо
4. @Makers_F: Как насчет стр. 127, учитывается ли x = 15 (возьмите ‘1’ и ‘7’ и вычтите ‘2’)? В вашем стр. 124 вы использовали только смежные цифры для формирования чисел (12 и 14), но можете ли вы также комбинировать несмежные цифры? Если вы можете, можете ли вы обратить их вспять? (скажем, 17, это считается, если у вас есть стр.71)?
5. @Makers_F: также, как насчет, скажем, страницы 1479, считается ли 52? (1 4) = 5 и (9-7) = 2, затем объединяем 5 и 2, чтобы получить 52?
Ответ №1:
Подсчитайте количество сумм, которые у вас будут с 1 <= страница # <= N, где N — количество страниц. Это намного меньше 10 000, потому что 1, 10, 100, 1000 и 10000 сопоставляются с суммой 1. Максимальное значение, которое у вас будет, равно 9999 => 36. Вы можете начать с карты, где страница # — это ключ, а сумма — значение, затем перевернуть ее и получить карту, где сумма — это ключ, а список страниц, сумма номеров которых равна ключу, — это значение.
Для 10 000 страниц все возможные суммы находятся в диапазоне от 1 до 36.
Поэтому, если вы выбираете случайное число из некоторого диапазона, используйте его в качестве ключа к перевернутой карте, чтобы получить список страниц, которые сопоставляются с этой суммой. Длина этого списка, деленная на количество страниц, — это вероятность, которую вы хотите.
Вот как я бы это сделал:
package misc;
import java.util.*;
/**
* PageSumProbability
*
* @author Michael
* @since 10/14/11
*/
public class PageSumProbability {
private Map<Integer, Integer> pageNumberSum;
private Map<Integer, List<Integer>> sumPageNumbers;
public static void main(String[] args) {
if (args.length > 1) {
int maxPageNumber = Integer.valueOf(args[0]);
int randomSum = Integer.valueOf(args[1]);
PageSumProbability psp = new PageSumProbability(maxPageNumber);
System.out.println(psp.getPageNumberSum());
System.out.println(psp.getSumPageNumbers());
System.out.printf("random sum: %d probability of opening page # that equals random sum: %5.3f%%n",
randomSum, 100*psp.getProbabilityOfSum(randomSum));
} else {
System.out.print("Usage: PageProbabilitySum <# pages> <random sum>");
}
}
public PageSumProbability(int maxPageNumber) {
this.pageNumberSum = new TreeMap<Integer, Integer>();
this.sumPageNumbers = new TreeMap<Integer, List<Integer>>();
for (int i = 1; i <= maxPageNumber; i) {
int sum = this.calculateSumOfDigits(i);
this.pageNumberSum.put(i, sum);
List<Integer> pages = this.sumPageNumbers.get(sum);
if (pages == null) {
pages = new LinkedList<Integer>();
}
pages.add(i);
this.sumPageNumbers.put(sum, pages);
}
}
public static int calculateSumOfDigits(int pageNumber) {
int sum = 0;
String pageNumberAsString = String.valueOf(Math.abs(pageNumber));
for (int i = 0; i < pageNumberAsString.length(); i) {
StringBuilder digit = new StringBuilder();
digit.append(pageNumberAsString.charAt(i));
sum = Integer.valueOf(digit.toString());
}
return sum;
}
public double getProbabilityOfSum(int randomSum) {
if (randomSum <= 0)
throw new IllegalArgumentException("random sum must be greater than zero");
double probability = 0.0;
List<Integer> pages = this.sumPageNumbers.get(randomSum);
if (pages != null) {
probability = (double) pages.size()/this.pageNumberSum.size();
}
return probability;
}
public Map<Integer, Integer> getPageNumberSum() {
return Collections.unmodifiableMap(this.pageNumberSum);
}
public Map<Integer, List<Integer>> getSumPageNumbers() {
return Collections.unmodifiableMap(this.sumPageNumbers);
}
}
Комментарии:
1. Действительно приятно! Я могу создать интерфейс с методом calculateCombination и передать его реализацию в качестве аргумента классу, чтобы он мог управлять несколькими типами систем сумм / вычитаний, как мне нужно
Ответ №2:
Вот как я бы это сделал.
Определите интерфейс DigitCombinationStragtegy
с помощью одного метода: Set<Integer> combineDigits(int pageNumber)
.
Напишите реализацию этого интерфейса для каждого способа объединения цифр: SumDigitCombinationStragtegy, SubstractionDigitCombinationStragtegy и т.д. Каждая стратегия возвращает набор комбинаций, которые она генерирует. На самом деле это самая сложная часть проблемы. Но реализовать только небольшие части проще, чем все целиком, и вы можете легко протестировать каждую стратегию.
Напишите реализацию этой стратегии, которая просто использует набор других стратегий и объединяет все наборы, которые они возвращают.
При запросе книги и числа N инициализируйте два числа 0: hits
и misses
. Создайте экземпляр соответствующей стратегии. Перебирайте страницы (или пары страниц) и запрашивайте у стратегии набор чисел, которые она генерирует. для этой страницы (или этой пары страниц). Если набор содержит N, выполняется увеличение. Иначе приращение не выполняется.
Вероятность равна попаданиям / (попаданиям промахам).
Комментарии:
1. Интересный метод, но проблема в том, что код не может быть повторно использован. Я имею в виду: на стр.1234 я не могу использовать реализацию simple sum для 12 и 34, но я должен определить новую реализацию, которая суммирует только число с «выбранной парой» (сначала 3, а затем 4, и, следовательно, вывод 15 и 16), а затем сделать это для всехдругие соседние пары.. Вот почему у меня возникает несколько проблем, объединение цифр таким «нематематическим» способом приводит к большой избыточности в коде без возможности его повторного использования, но это то, что мне нужно..