To understand the difference between polynomial time and pseudopolynomial time, we need to start off by formalizing what "polynomial time" means.

The common intuition for polynomial time is "time O(nk) for some k." For example, selection sort runs in time O(n2), which is polynomial time, while brute-force solving TSP takes time O(n · n!), which isn't polynomial time.

These runtimes all refer to some variable n that tracks the size of the input. For example, in selection sort, n refers to the number of elements in the array, while in TSP n refers to the number of nodes in the graph. In order to standardize the definition of what "n" actually means in this context, the formal definition of time complexity defines the "size" of a problem as follows:

The size of the input to a problem is the number of bits required to write out that input.

For example, if the input to a sorting algorithm is an array of 32-bit integers, then the size of the input would be 32n, where n is the number of entries in the array. In a graph with n nodes and m edges, the input might be specified as a list of all the nodes followed by a list of all the edges, which would require Ω(n + m) bits.

Given this definition, the formal definition of polynomial time is the following:

An algorithm runs in polynomial time if its runtime is O(xk) for some constant k, where x denotes the number of bits of input given to the algorithm.

When working with algorithms that process graphs, lists, trees, etc., this definition more or less agrees with the conventional definition. For example, suppose you have a sorting algorithm that sorts arrays of 32-bit integers. If you use something like selection sort to do this, the runtime, as a function of the number of input elements in the array, will be O(n2). But how does n, the number of elements in the input array, correspond to the the number of bits of input? As mentioned earlier, the number of bits of input will be x = 32n. Therefore, if we express the runtime of the algorithm in terms of x rather than n, we get that the runtime is O(x2), and so the algorithm runs in polynomial time.

Similarly, suppose that you do depth-first search on a graph, which takes time O(m + n), where m is the number of edges in the graph and n is the number of nodes. How does this relate to the number of bits of input given? Well, if we assume that the input is specified as an adjacency list (a list of all the nodes and edges), then as mentioned earlier the number of input bits will be x = Ω(m + n). Therefore, the runtime will be O(x), so the algorithm runs in polynomial time.

Things break down, however, when we start talking about algorithms that operate on numbers. Let's consider the problem of testing whether a number is prime or not. Given a number n, you can test if n is prime using the following algorithm:

function isPrime(n):
for i from 2 to n - 1:
if (n mod i) = 0, return false
return true

So what's the time complexity of this code? Well, that inner loop runs O(n) times and each time does some amount of work to compute n mod i (as a really conservative upper bound, this can certainly be done in time O(n3)). Therefore, this overall algorithm runs in time O(n4) and possibly a lot faster.

In 2004, three computer scientists published a paper called PRIMES is in P giving a polynomial-time algorithm for testing whether a number is prime. It was considered a landmark result. So what's the big deal? Don't we already have a polynomial-time algorithm for this, namely the one above?

Unfortunately, we don't. Remember, the formal definition of time complexity talks about the complexity of the algorithm as a function of the number of bits of input. Our algorithm runs in time O(n4), but what is that as a function of the number of input bits? Well, writing out the number n takes O(log n) bits. Therefore, if we let x be the number of bits required to write out the input n, the runtime of this algorithm is actually O(24x), which is not a polynomial in x.

This is the heart of the distinction between polynomial time and pseudopolynomial time. On the one hand, our algorithm is O(n4), which looks like a polynomial, but on the other hand, under the formal definition of polynomial time, it's not polynomial-time.

To get an intuition for why the algorithm isn't a polynomial-time algorithm, think about the following. Suppose I want the algorithm to have to do a lot of work. If I write out an input like this:

10001010101011

then it will take some worst-case amount of time, say T, to complete. If I now add a single bit to the end of the number, like this:

100010101010111

The runtime will now (in the worst case) be 2T. I can double the amount of work the algorithm does just by adding one more bit!

An algorithm runs in pseudopolynomial time if the runtime is some polynomial in the numeric value of the input, rather than in the number of bits required to represent it. Our prime testing algorithm is a pseudopolynomial time algorithm, since it runs in time O(n4), but it's not a polynomial-time algorithm because as a function of the number of bits x required to write out the input, the runtime is O(24x). The reason that the "PRIMES is in P" paper was so significant was that its runtime was (roughly) O(log12 n), which as a function of the number of bits is O(x12).

So why does this matter? Well, we have many pseudopolynomial time algorithms for factoring integers. However, these algorithms are, technically speaking, exponential-time algorithms. This is very useful for cryptography: if you want to use RSA encryption, you need to be able to trust that we can't factor numbers easily. By increasing the number of bits in the numbers to a huge value (say, 1024 bits), you can make the amount of time that the pseudopolynomial-time factoring algorithm must take get so large that it would be completely and utterly infeasible to factor the numbers. If, on the other hand, we can find a polynomial-time factoring algorithm, this isn't necessarily the case. Adding in more bits may cause the work to grow by a lot, but the growth will only be polynomial growth, not exponential growth.

That said, in many cases pseudopolynomial time algorithms are perfectly fine because the size of the numbers won't be too large. For example, counting sort has runtime O(n + U), where U is the largest number in the array. This is pseudopolynomial time (because the numeric value of U requires O(log U) bits to write out, so the runtime is exponential in the input size). If we artificially constrain U so that U isn't too large (say, if we let U be 2), then the runtime is O(n), which actually is polynomial time. This is how radix sort works: by processing the numbers one bit at a time, the runtime of each round is O(n), so the overall runtime is O(n log U). This actually is polynomial time, because writing out n numbers to sort uses Ω(n) bits and the value of log U is directly proportional to the number of bits required to write out the maximum value in the array.

Hope this helps!

What is pseudopolynomial time? How does it differ from polynomial time?的更多相关文章

  1. How threads differ from processes

    How threads differ from processes Threads differ from traditional multitasking operating system proc ...

  2. Atitti css transition Animation differ区别

    Atitti  css   transition Animation differ区别 1.1. transition的优点在于简单易用,但是它有几个很大的局限.  1 1.2. Transition ...

  3. Atitti  css   transition Animation differ区别

    Atitti  css   transition Animation differ区别 1.1. transition的优点在于简单易用,但是它有几个很大的局限.  1 1.2. js 动态改变 st ...

  4. 【转载】#274 - Can't Overload if Methods Differ Only by ref and out Modifiers

    You can overload a method in a class, i.e. define two methods with the same name, if the methods hav ...

  5. differ比较两个字符串的差异

    "abcde","abdefk"  ---->-c,+f,+k "aba","aababb"    -----&g ...

  6. Angular中 build的时候遇到的错误--There are multiple modules with names that only differ in casing

    今天早上遇到一个Angular的编译的时候的错误 具体信息: There are multiple modules with names that only differ in casing.This ...

  7. There are multiple modules with names that only differ in casing. 黄色warning

    There are multiple modules with names that only differ in casing.有多个模块同名仅大小写不同This can lead to unexp ...

  8. Conflict with dependency 'com.android.support:support-annotations' in project ':xxx'. Resolved versions for app (25.4.0) and test app (27.1.1) differ 问题解决

    Conflict with dependency 'com.android.support:support-annotations' in project ':xxx'. Resolved versi ...

  9. vue项目警告There are multiple modules with names that only differ in casing

    执行npm run dev后出现了警告提示: warning in ./src/components/Public/yearSelectCell.vue There are multiple modu ...

随机推荐

  1. 【上下界网络流 二分】bzoj2406: 矩阵

    感觉考试碰到上下界网络流也还是写不来啊 Description Input 第一行两个数n.m,表示矩阵的大小. 接下来n行,每行m列,描述矩阵A. 最后一行两个数L,R. Output 第一行,输出 ...

  2. XML 转 fastJSON

      import java.util.List; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Doc ...

  3. virtualbox安装win7系统报错(“FATAL:No bootable medium found!”)

    virtualbox属于傻瓜式安装虚拟系统,但博主安装win7系统时,无论怎么调试都还是出现截图所述样式,网上教程很多,但是都不行,其实只有一个根本原因安装的iso镜像不是原生镜像,下载的镜像已经是被 ...

  4. tp5使用外部类的三种方法

    在tp5中使用外部类的时候有三种方法 第一种就是通过composer下载,通过这种方式下载的外部类能够支持自动加载,我们只要在使用的时候use一下命名空间就可以使用了 比如:我们的tp5第四季项目要使 ...

  5. redis主从+哨兵模式

    主从模式配置分为手动和配置文件两种方式进行配置,我现在有192.168.238.128(CentOS1).192.168.238.131(CentOS3).192.168.238.132(CentOS ...

  6. 使用 Python 编写登陆接口

    # 使用 Python 编写登陆接口# Create Date: 2017.10.31 Tuesday# Author: Eric Zhao# -*- coding:utf-8 -*-'''编写登陆接 ...

  7. python里字典的用法介绍

    一.什么是字典 字典是python里的一种数据类型,特点是元素的无序性,和键key的唯一性.字典的创建方法是{key:values},字典里的键key只能是不可变的数据类型(整型,字符串或者是元组), ...

  8. django中的objects.get和objects.filter方法的区别

    为了说明它们两者的区别定义2个models class Student(models.Model): name = models.CharField('姓名', max_length=20, defa ...

  9. mysql 索引和查询优化

    对于任何DBMS,索引都是进行优化的最主要的因素.对于少量的数据,没有合适的索引影响不是很大,但是,当随着数据量的增加,性能会急剧下降.如果对多列进行索引(组合索引),列的顺序非常重要,MySQL仅能 ...

  10. python - 接口自动化 - 接口测试基础知识

    # -*- coding:utf-8 -*- '''@project: jiaxy@author: Jimmy@file: study_接口测试基础知识一.py@ide: PyCharm Commun ...