初等数论与概率论相关题目

1.初等数论

  • 算术基本定理:任何一个大于1的自然数N,如果N不为质数,都可以唯一分解成有限个质数的乘积:



    关于任意两个数x和y的最大公约数和最小公倍数:







    并且如果x整除y,则ji <= ki对于所有i都成立。

  • 素数检查
    1)如果a是一个合数,则a必有一个小于等于根号a的真因子。
    2)如果a是一个合数,则a必有一个小于等于根号a的素因子
    3)埃拉托斯特尼筛法:
    首先划掉被2整除的;
    然后找到下一个整数,划掉所有可能被它整除的;
    继续,依次类推

public class SieveOfEratosthenes {
    
    public static void crossOff(boolean[] flags, int prime) {
        /* Cross off remaining multiples of prime. We can start with
         * (prime*prime), because if we have a k * prime, where k < prime,
         * this value would have already been crossed off in a prior
         * iteration. */        
        for (int i = prime * prime; i < flags.length; i += prime) {
            flags[i] = false;
        }
    }
    
    public static int getNextPrime(boolean[] flags, int prime) {
        int next = prime + 1;
        while (next < flags.length && !flags[next]) {
            next++;
        }
        return next;    
    }
    
    public static void init(boolean[] flags) {
        flags[0] = false;
        flags[1] = false;
        for (int i = 2; i < flags.length; i++) {
            flags[i] = true;
        }
    }
    
    public static int[] prune(boolean[] flags, int count) {
        int[] primes = new int[count];
        int index = 0;
        for (int i = 0; i < flags.length; i++) {
            if (flags[i]) {
                primes[index] = i;
                index++;
            }
        }
        return primes;
    }
    
    public static boolean[] sieveOfEratosthenes(int max) {
        boolean[] flags = new boolean[max + 1];
        int count = 0;
        
        init(flags);
        int prime = 2;
        
        while (prime <= Math.sqrt(max)) {
            count++;            
            crossOff(flags, prime);
            prime = getNextPrime(flags, prime);
            if (prime >= flags.length) {
                break;
            }
        }
        
        return flags; //prune(flags, count);
    }
    

2.概率

与 或 独立 互斥

3.题目

Q1.有个篮球框,下面两种玩法可任选一种。
玩法1:一次出手机会,投篮命中得分。
玩法2:三次出手机会,必须投中两次。
如果p是某次投篮命中的概率,则p的值为多少时,才会选择玩法1或玩法2?

Ans.思路:

  • 玩法一赢的概率为p
  • 玩法二赢的概率为三投三中 + 三投两中 = p^3 + 3(1-p)p^2
  • p > p^3 + 3(1-p)p^2
    => (2p - 1) (p - 1) > 0
    => p < 0.5
    若p < 0.5 则应选择玩法1,p = 0 0.5 1时,两种都可以,否则选择玩法2.

Q2.三角形的三个顶点上各有一只蚂蚁。如果蚂蚁开始沿着三角形的边爬行,两只或三只蚂蚁撞在一起的概率有多大?假定每只蚂蚁会随机选一个方向,每个方向被选到的几率相等,而且三只蚂蚁的爬行速度相同。
类似问题:在n个顶点的多边形上有n只蚂蚁,求出这些蚂蚁发生碰撞的概率。

Ans.思路:

  • 总的可能性:每个蚂蚁又可以有两种方向选择,所以总共有2^3 = 8种可能。不会发生碰撞的情况有两种:三只蚂蚁都选择同一个方向。因此发生碰撞的概率 = 1 - 1/4 = 3/4
  • n只的情况 1 - 2(1/2)^n

Q3.给定直角坐标系上的两条线,确定这两条线会不会相交。

Ans.思路:

  • 若两条线为同一条,则认为这两条线相交
  • 若两条直线平行则必不相交
  • 因此关键是判断是否为同一条或者是否平行
public class Line {
    static double epsilon = 0.000001;
    public double slope;
    public double yintercept;
    
    public Line(double s, double y) {
        slope = s;
        yintercept = y;
    }
    
    public void print() {
        System.out.print("y = " + slope + "x + " + yintercept);
    }
    
    public boolean intersect(Line line2) {
        return Math.abs(slope - line2.slope) > epsilon ||
               Math.abs(yintercept - line2.yintercept) < epsilon;
    }
};

Q4.编写方法,实现整数的乘法、减法和除法运算。只允许使用加号。

Ans.思路:

  • 减法换为加法,加的另一个数为相反数,相反数可以通过累加-1最后与原数相加为0得到
  • 乘法为多次累加同一个数
  • 除法转换为累加次数,累加到目标值
  • 关键是不要假设都是整数,某个数比某个数大
public class Question {
    /* Flip a positive sign to negative, or a negative sign to pos */
    public static int negate(int a) {
        int neg = 0;
        int d = a < 0 ? 1 : -1;
        while (a != 0) {
            neg += d;
            a += d;
        }
        return neg;
    }

    /* Subtract two numbers by negating b and adding them */
    public static int minus(int a, int b) {
        return a + negate(b);
    }

    /* Return absolute value */
    public static int abs(int a) {
        if (a < 0) {
            return negate(a);
        }
        else return a;
    }

    /* Multiply a by b by adding a to itself b times */
    public static int multiply(int a, int b) {
        if (a < b) {
            return multiply(b, a); // algo is faster if b < a
        }
        int sum = 0;
        for (int i = abs(b); i > 0; i--) {
            sum += a;
        }
        if (b < 0) {
            sum = negate(sum);
        }
        return sum;
    }

    /* Divide a by b by literally counting how many times b can go into
     * a. That is, count how many times you can add b to itself until you reach a. */
    public static int divide(int a, int b) throws java.lang.ArithmeticException {
        if (b == 0) {
            throw new java.lang.ArithmeticException("ERROR: Divide by zero.");
        }
        int absa = abs(a);
        int absb = abs(b);
        
        int product = 0;
        int x = 0;
        while (product + absb <= absa) { /* don't go past a */
            product += absb;
            x++;
        }
        
        if ((a < 0 && b < 0) || (a > 0 && b > 0)) {
            return x;
        } else {
            return negate(x);
        }
    }

Q5.在二维平面上,有两个正方形,请找出一条直线,能够将这两个正方形对半分。假定正方形的上下两条边与x轴平行。

Ans.思路:

  • 这条线必须连接两个正方形中心点。必须注意特殊情况,比如中心点重合。

Q6.在二维平面上,有一些点,请找出经过点数最多的那条线。

Ans.思路:

  • 只需遍历所有线段(n - 1)n/2,并用散列表统计出每条直线出现的次数。
  • 斜率可能是初值无限大,需要单独记录
  • 浮点数不能精确表示的问题,检查浮点数差值是否在某个极小值范围内。首先对斜率进行floor,即变成epsilon的整数倍。然后搜索时,搜索三个位置:floor, floor-epsilon, floor+epsilon
public class GraphPoint {
    public double x;
    public double y;
    public GraphPoint(double x1, double y1) {
        x = x1;
        y = y1;
    }
    
    public String toString() {
        return "(" + x + ", " + y + ")";
    }
}
public class Line {
    public static double epsilon = .0001;
    public double slope;
    public double intercept;
    
    private boolean infinite_slope = false;
    
    public Line(GraphPoint p, GraphPoint q) {
        if (Math.abs(p.x - q.x) > epsilon) { // if x�s are different
            slope = (p.y - q.y) / (p.x - q.x); // compute slope
            intercept = p.y - slope * p.x; // y intercept from y=mx+b
        } else {
            infinite_slope = true;
            intercept = p.x; // x-intercept, since slope is infinite
        }
    }
    
    public boolean isEquivalent(double a, double b) {
        return (Math.abs(a - b) < epsilon);
    }
    
    public void Print() {
        System.out.println("y = " + slope + "x + " + intercept);
    }
        
    public static double floorToNearestEpsilon(double d) {
        int r = (int) (d / epsilon);
        return ((double) r) * epsilon;
    }
    
    public boolean isEquivalent(Object o) {  
        Line l = (Line) o;
        if (isEquivalent(l.slope, slope) && isEquivalent(l.intercept, intercept) && (infinite_slope == l.infinite_slope)) {
            return true;
        }
        return false;
    }      
}
public class Question { 
    
    /* Count lines within an array of lines which are "equivalent" (slope and y-intercept are within an epsilon value) to a given line */
    public static int countEquivalentLines(ArrayList<Line> lines, Line line) {
        if (lines == null) {
            return 0;
        }
        
        int count = 0;
        for (Line parallelLine : lines) {
            if (parallelLine.isEquivalent(line)) {
                count++;
            }
        }
        return count;       
    }
    
    /* Check hashmap for lines that are equivalent. Note that we need to check one epsilon above and below the actual slope
     * since we're defining two lines as equivalent if they're within an epsilon of each other.
     */
    public static int countEquivalentLines(HashMap<Double, ArrayList<Line>> linesBySlope, Line line) {
        double key = Line.floorToNearestEpsilon(line.slope);
        int count = countEquivalentLines(linesBySlope.get(key), line);
        count += countEquivalentLines(linesBySlope.get(key - Line.epsilon), line);
        count += countEquivalentLines(linesBySlope.get(key + Line.epsilon), line);
        return count;
    }   
    
    /* insert line into hashmap */
    public static void insertLine(HashMap<Double, ArrayList<Line>> linesBySlope, Line line) {
        ArrayList<Line> lines = null;
        double key = Line.floorToNearestEpsilon(line.slope);
        if (!linesBySlope.containsKey(key)) {
            lines = new ArrayList<Line>();
            linesBySlope.put(key, lines);
        } else {
            lines = linesBySlope.get(key);
        }
        lines.add(line);
    }
    
    
    public static Line findBestLine(GraphPoint[] points) {
        Line bestLine = null;
        int bestCount = 0;
        HashMap<Double, ArrayList<Line>> linesBySlope = new HashMap<Double, ArrayList<Line>>();
        
        for (int i = 0; i < points.length; i++) {
            for (int j = i + 1; j < points.length; j++) {
                Line line = new Line(points[i], points[j]);
                insertLine(linesBySlope, line);
                
                /* count lines that are equivalent to current line */
                int count = countEquivalentLines(linesBySlope, line);
                
                /* if better than current line, replace it */
                if (count > bestCount) {
                    bestLine = line;
                    bestCount = count;
                }
            }
        } 
        
        return bestLine;
    }   
    
    public static GraphPoint[] createPoints() {
        int n_points = 100;
        System.out.println("Points on Graph\n***************");
        GraphPoint[] points = new GraphPoint[n_points - 1];
        for (int i = 0; i < n_points / 2; i++) {
            GraphPoint p = new GraphPoint(i, 2.3 * ((double)i) + 20.0);
            points[i] = p;
            System.out.println(p.toString());
        }
        for (int i = 0; i < n_points / 2 - 1; i++) {
            GraphPoint p = new GraphPoint(i, 3.0 * ((double)i) + 1.0);
            points[n_points / 2 + i] = p;
            System.out.println(p.toString());
        }
        System.out.println("****************\n");
        return points;
    }

Q7.有些数的素因子只有3、5、7,请设计一个算法,找出其中第k个数。

Ans.思路:

  • 一种直观的思路,从3开始,然后将列表中的数字与3/5/7相乘,找出下一个数。3(1 + 2 + ... + k - 1) = O(k^2)
  • 同样的本质,换个思路:每次要将Ai加入列表时,就用某个临时表存放3Ai、5Ai和7Ai三个值。要产生Ai+1时,会搜索这个临时表
  • 优化:按1常数因子将列表分组存放,那就只需检查3 、5和7倍数的第一个。求出最小的y后,将3y插入Q3、 5y插入Q5、 7y插入Q7,不过只有这些元素在其他列表中不存在时,才会将它们插入列表。
    例如从Q7中取出当前最小的7*suffix,此时肯定已经取出了3*suffix和5*suffix,处理3*suffix时,已经将7*3*suffix加入了Q7,处理5*suffix时,已经将7*5*suffix加入了Q7。所以此时只会将7 * 7 *suffix加入Q7。




    public static void printQueue(Queue<Integer> q, int x) {
        System.out.print(x + ": ");
        for (Integer a : q) {
            System.out.print(a / x + ", ");
        }
        System.out.println("");
    }
    
    public static int getKthMagicNumber(int k) {
        if (k < 0) {
            return 0;
        }
        int val = 0;
        Queue<Integer> queue3 = new LinkedList<Integer>();
        Queue<Integer> queue5 = new LinkedList<Integer>();
        Queue<Integer> queue7 = new LinkedList<Integer>();
        queue3.add(1);
        for (int i = 0; i <= k; i++) { // Include 0th iteration through kth iteration
            int v3 = queue3.size() > 0 ? queue3.peek() : Integer.MAX_VALUE; 
            int v5 = queue5.size() > 0 ? queue5.peek() : Integer.MAX_VALUE;
            int v7 = queue7.size() > 0 ? queue7.peek() : Integer.MAX_VALUE;
            val = Math.min(v3, Math.min(v5, v7));
            if (val == v3) {
                queue3.remove();
                queue3.add(3 * val);
                queue5.add(5 * val);
            } else if (val == v5) {
                queue5.remove();
                queue5.add(5 * val);
            } else if (val == v7) {
                queue7.remove();
            }
            queue7.add(7 * val);
        }
        return val;
    }

Q8.编写一个方法,洗一副牌。要求做到完美洗牌,换言之,这副牌52!种排列组合出现的概率相同。假设给定一个完美的随机数发生器。

Ans.思路:

  • 假设前n-1个元素已经随机打乱了,那么用它来打乱n个元素可行吗?
    可以,只要第n个元素与数组中元素随机交换。
  • 证明:前n-1取某一种排列的概率1/(n-1)!,第n个元素从n个位置中随机取一个位置的概率为1/n,两者相乘就是该排列的概率,就是1/n!。
  • 那怎样确保所有这种排列都是不同的?前面(n-1)!排列是不同,再在后面接一个n也是不同的;
    现在可能相同的是,n同前面的某个数置换过后,将某个相同的数置换到第n的位置上。将n-1中某个数替换为n,放到n位置上,跟n-1个数的随机排列无本质区别,所以还是不同!


    /* Random number between lower and higher, inclusive */
    public static int rand(int lower, int higher) { 
        return lower + (int)(Math.random() * (higher - lower + 1));
    }   
    
    public static int[] shuffleArrayRecursively(int[] cards, int i) {
        if (i == 0) {
            return cards;
        }
        
        /* shuffle elements 0 through index - 1 */
        shuffleArrayRecursively(cards, i - 1);
        int k = rand(0, i);     
        
        /* Swap element k and index */
        int temp = cards[k];
        cards[k] = cards[i];
        cards[i] = temp;
        
        /* Return shuffled array */
        return cards;
    }
    
    public static void shuffleArrayInteratively(int[] cards) { 
        for (int i = 0; i < cards.length; i++) { 
            int k = rand(0, i);
            int temp = cards[k];
            cards[k] = cards[i];
            cards[i] = temp;
        } 
    }

Q9.编写一个算法,从大小为n的整数数组中随机选出m个整数。要求每个元素被选中的概率相同。

Ans.思路:

  • 先取前m个数,然后i从m到n,每次取随机数(0, i)——k,如若k小于m,则将前面第k位置换为original[i]。
  • 证明:跟第8题本质上是一样的。可以依据前面的算法,随机排列,然后返回前m个数。只不过前面m个数的随机排列不需要,另外,超过m的交换也不需要。
    /* pick M elements from original array.*/
    public static int[] pickMIteratively(int[] original, int m) {
        int[] subset = new int[m];
        
        /* Fill in subset array with first part of original array */
        for (int i = 0; i < m ; i++) {
            subset[i] = original[i];
        }
        
        /* Go through rest of original array. */
        for (int i = m; i < original.length; i++) {
            int k = rand(0, i);
            if (k < m) {
                subset[k] = original[i];
            }
        }
        
        return subset;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • 你的数学直觉怎么样?你能凭借直觉,迅速地判断出谁的概率大,谁的概率小吗?下面就是 26 个这样的问题。如果你感兴趣...
    cnnjzc阅读 6,829评论 0 12
  • 各位小伙伴们大家好,前些日子,我看了一些关于贝叶斯方法的文章,其中以今天这一篇文章觉得最好,不仅讲的简单通俗易懂并...
    云时之间阅读 5,626评论 4 72
  • 对于 D 题的原题意,出题人和验题人赛前都没有发现标算存在的问题,导致了许多选手的疑惑和时间的浪费,在此表示真诚的...
    _Carryon阅读 251评论 0 0
  • 王总,你好! 我简单介绍下自己,让你对我有个初步的了解。上次见面时间也比较紧张,主要聊了些概略的构想,希望以后有时...
    祁国瑜阅读 766评论 0 0
  • JS下载解析时候会阻塞DOM树的构建,放在HTML顶部 的时候会有可能出现长时间白屏的情况,想让JS解析时候阻塞D...
    kitssang阅读 852评论 0 0