Jadual Kandungan
例子:3-sum与2-sum计算" >例子:3-sum与2-sum计算
成本模型" >成本模型
从成本模型出发优化程序" >从成本模型出发优化程序
增长数量级/时间复杂度" >增长数量级/时间复杂度
常数级别" >常数级别
对数级别" >对数级别
线性级别" >线性级别
线性对数级别" >线性对数级别
平方级别" >平方级别
立方级别" >立方级别
指数级别" >指数级别
优化2-sum问题" >优化2-sum问题
优化优化算法的注意事项" >优化优化算法的注意事项
大常数" >大常数
错误的成本模型" >错误的成本模型
Rumah Java javaTutorial 关于java时间计算的算法与优化方法详解

关于java时间计算的算法与优化方法详解

May 07, 2017 am 09:44 AM

随着使用计算机的经验的增长,人们在使用计算机编写程序的时候,不可避免的会发出这样的疑问:

我的程序运行一次需要多久?
 我的代码是否可以再优化得更快更节省空间?

当我们打开一个网页或者传输一个文件或打开一个播放器时,你也肯定问过自己上面的问题。但是在这种情况下估计时间和数据处理的复杂度太难太模糊了。相比较这种大型应用,我们能够处理的是单个程序的复杂度和效率。如果每片程序的效率都是相对较优的,那么我们也不用需要担心最后组合的应用会过于臃肿缓慢。这篇文章会对程序的算法时空复杂度,优化方法和等等一切琐事做解释,从3-sum和2-sum这两个例子开始,从头到尾演示算法是怎么对我们的程序起作用的。

例子:3-sum与2-sum计算


3-sum
 一个文件中有许多不重复整数,任意三个整数可组成一组,统计其中任意三个整数和为0的组数

2-sum
 一个文件中有许多不重复整数,统计其中和为0的整数对个数

暴力算法(常规未优化算法):

  public int threeSum(int[] arr) {
        int count = 0;
        int n = arr.length;
        for (int i = 0; i < n; i++)
            for (int j = i + 1; j < n; j++)
                for (int k = j + 1; k < n; k++)
                    if (arr[i] + arr[j] + arr[k] == 0)
                        count++;
        return count;
    }
Salin selepas log masuk
<p style="margin-bottom: 7px;">  public int twoSum(int[] arr) {<br/>        int count = 0;<br/>        int n = arr.length;<br/>        for (int i = 0; i < n; i++)<br/>            for (int j = i + 1; j < n; j++)<br/>                if (arr[i] == -arr[j])<br/>                    count++;<br/>        return count;<br/>    }<br/></p>
Salin selepas log masuk

成本模型


我们如果想要优化这段程序,首先要知道这段程序能优化的点在哪里,这就是评估成本模型。通过确定程序的成本模型来定义我们所研究的算法中的基本操作。

3-sum问题的成本模型
在研究解决3-sum问题的算法时,我们记录的是数组的访问次数(访问数组次数,无论读写)

换而言之,我们的目标就是尽量让我们评估出的成本模型变得更优。在3-sum问题中模型比较简单,但有些复杂的程序的成本模型需要谨慎确定,因为如果我们忽略了一些操作可能导致最后优化的结果并不是程序整体的最优情况。

从成本模型出发优化程序


上面的暴力代码没有任何在结果上的问题,在数据较少的情况下执行的还不算太慢。但是如果测试数据量是10的n次方级,那么这种未经优化的代码在时间上会带来很大的负面效果。我们说暴力的代码速度慢,那什么样的代码才算是速度快呢?

我们在3-sum的成本模型中已经确定了数组的访问次数是我们要研究和优化的内容。在java中,对数组的访问消耗的时间是常量级的(常量级就是几乎不消耗时间的意思)。那么在访问数组内容时间固定的情况下,让数组的访问次数变少,我们只需要让代码运行的时间变快即可。为了让代码速度变快,首先我们要了解增长数量级的分类和时间复杂度的概念。

增长数量级/时间复杂度


我们要说的内容就是时间复杂度,但是我们给他换了个名字——增长数量级。除了不使用大O标记,其他的地方这两种并没有什么区别。我们在实现算法的时候使用了几种结构性的原语,比如普通语句,循环条件判断,嵌套语句等等,所以成本增长的数量级一般都是问题规模N的某个函数。问题规模N在这个例子中就是数组中数字的个数。

增长数量级/时间复杂度主要有以下7种:

常数级别

增长的数量级: 1
时间复杂度: O(1)
典型代码:

a = b + c;
Salin selepas log masuk

结构性原语:普通语句
举例:将两个数相加
说明:运行时间的增长数量级为常数级别的程序完成他的任务需要的时间是固定的,和N无关。java中几乎所有的操作所需的时间都是常数级别的。常数级别的速度是所有时间复杂度中最快的。

对数级别

增长的数量级: logN
时间复杂度: O(logN)
典型代码:

public static int rank(int key, int[] a, int lo, int hi) {
        if (hi < lo)
            return -1;
        int mi = lo + (hi - lo) / 2;
        if (key < a[mi])
            return rank(key, a, lo, mi - 1);
        else if (key > a[mi])
            return rank(key, a, mi + 1, hi);
        else
            return mi;
    }
Salin selepas log masuk

结构性原语:二分策略
举例:二分查找
说明:速度稍微慢于常数级别,但快于O(N)最典型的对数级别的例子就是二分查找,log下面的底数可能是变化的,但是因为只是一个常数对整体和N的影响不大,所以我们一般表示成logN的形式。

线性级别

增长的数量级: N
时间复杂度: O(N)
典型代码:

for (int i = 0; i < n; i++)
        if(arr[i] == 0)
            count++;
Salin selepas log masuk

结构性原语: 一层循环
举例: 找出最大元素
说明: 线性级别稍慢于对数级别,但快于线性对数级别。他的运行时间和N成正比。在有些情况下,我们只需要进行一半的数组遍历,理论上可以将时间除以2,但是仍旧与N的一次方有关,所以我们把此类都算作线性级别。

线性对数级别

增长的数量级: NlogN
时间复杂度: O(NlogN)
典型代码:
归并排序
结构性原语: 分治
举例: 归并排序
说明: 线性N乘以对数logN,速度快于N2但是慢于线性级别。在归并排序中,按照类似二分的方法确定归并点,对归并到最底层的数据进行复杂度为O(N)的排序,时间复杂度为O(NlogN)。快速排序,归并排序都可以看作是线性对数级别。

平方级别

增长的数量级: N2
时间复杂度: O(N2)
典型代码:

for (int i = 0; i < n; i++)
            for (int j = i + 1; j < n; j++)
                if (arr[i] == -arr[j])
                    count++;
Salin selepas log masuk

结构性原语:双层循环
举例:检查所有元素对
说明:平方级别的程序一般都是含有两个for循环,初级的排序算法选择排序和插入排序都是这种复杂度。

立方级别

增长的数量级: N3
时间复杂度: O(N3)
典型代码:

for (int i = 0; i < n; i++)  for (int j = i + 1; j < n; j++)
        for (int k = j + 1; k < n; k++)
            if (arr[i] + arr[j] + arr[k] == 0)
                count++;
Salin selepas log masuk

结构性原语:三层循环
举例:对数组中的三个数据进行操作
说明:立方级别的代码一般都是三个for循环嵌套而成,我们日常的算法很少有这种运算时长。

指数级别

增长的数量级: 2N
时间复杂度: O(2N)
典型代码:
穷举查找所有真子集
结构性原语:穷举法
举例:穷举查找所有真子集
说明:指数级别的增长在N较大时比较可怕,但是它确是某些问题看起来最优的解法,并不是很常用,等如果需要学习的时候可以专门研究。

优化2-sum问题


先从2-sum问题入手,根据上述复杂度,2-sum问题的暴力解法复杂度为O(N2),又根据我们的成本模型只考虑访问数组次数,所以我们要在O(N2)的复杂度之内寻求最优解法即可。

从上面得知,排序算法的复杂度是O(NlogN)快于O(N2),而我们对排序之后的数据进行运算时,已经不是在排序内部进行处理。所以最后计算时间是用加法而不是乘法。所以我们最后的解决策略是:我们先对整个数组进行排序,然后从数组头部依次读取a[i],在数组中检索是否存在-a[i]。这种检索采用了二分查找的方法,复杂度为O(logN)。为了避免重复查找,如果查找到-a[i]的位置在a[i]之前,就说明该元素对已经被查找过,不计入次数。这样整体的算法复杂度是NlogN+N*logN(不知道这样写是否规范)。两项合并系数忽略,所以最后算法整体的复杂度为O(NlogN)。

public int twoSum(int[] arr) {
        int count = 0;
        Arrays.sort(arr);
        for (int i = 0; i < arr.length; i++)
            if (Arrays.binarySearch(arr, -arr[i]) > i)
                count++;
        return count;
    }
Salin selepas log masuk

2-sum问题已经成功的进行了优化,3-sum问题也可以通过这种方式进行优化,但是最后优化的复杂度为O(N2logN),感兴趣的朋友可以自己试一试,或者有更简便算法的话,发在评论里我们交流一下。不胜感激。

优化优化算法的注意事项


大常数

在首项近似中,我们一般会忽略低级项中的常数系数,但这可能是错的。比如,我们把函数2N2+cN的近似为 ~ 2N2时,我们的假设是c很小。但如果c可能是10的一个很高次方,该近似是错误的。我们要对可能很大的常数保持敏感。

错误的成本模型

内循环是决定性因素的假设不总是正确的。错误的成本模型可能无法得到真正的内循环,也有可能对循环中的某些语句没有足够的重视,导致时间估计的错误。总而言之,就是成本模型需要改进。

Atas ialah kandungan terperinci 关于java时间计算的算法与优化方法详解. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Cara Membuka Segala -galanya Di Myrise
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Nombor Sempurna di Jawa Nombor Sempurna di Jawa Aug 30, 2024 pm 04:28 PM

Panduan Nombor Sempurna di Jawa. Di sini kita membincangkan Definisi, Bagaimana untuk menyemak nombor Perfect dalam Java?, contoh dengan pelaksanaan kod.

Penjana Nombor Rawak di Jawa Penjana Nombor Rawak di Jawa Aug 30, 2024 pm 04:27 PM

Panduan untuk Penjana Nombor Rawak di Jawa. Di sini kita membincangkan Fungsi dalam Java dengan contoh dan dua Penjana berbeza dengan contoh lain.

Weka di Jawa Weka di Jawa Aug 30, 2024 pm 04:28 PM

Panduan untuk Weka di Jawa. Di sini kita membincangkan Pengenalan, cara menggunakan weka java, jenis platform, dan kelebihan dengan contoh.

Nombor Smith di Jawa Nombor Smith di Jawa Aug 30, 2024 pm 04:28 PM

Panduan untuk Nombor Smith di Jawa. Di sini kita membincangkan Definisi, Bagaimana untuk menyemak nombor smith di Jawa? contoh dengan pelaksanaan kod.

Soalan Temuduga Java Spring Soalan Temuduga Java Spring Aug 30, 2024 pm 04:29 PM

Dalam artikel ini, kami telah menyimpan Soalan Temuduga Spring Java yang paling banyak ditanya dengan jawapan terperinci mereka. Supaya anda boleh memecahkan temuduga.

Cuti atau kembali dari Java 8 Stream Foreach? Cuti atau kembali dari Java 8 Stream Foreach? Feb 07, 2025 pm 12:09 PM

Java 8 memperkenalkan API Stream, menyediakan cara yang kuat dan ekspresif untuk memproses koleksi data. Walau bagaimanapun, soalan biasa apabila menggunakan aliran adalah: bagaimana untuk memecahkan atau kembali dari operasi foreach? Gelung tradisional membolehkan gangguan awal atau pulangan, tetapi kaedah Foreach Stream tidak menyokong secara langsung kaedah ini. Artikel ini akan menerangkan sebab -sebab dan meneroka kaedah alternatif untuk melaksanakan penamatan pramatang dalam sistem pemprosesan aliran. Bacaan Lanjut: Penambahbaikan API Java Stream Memahami aliran aliran Kaedah Foreach adalah operasi terminal yang melakukan satu operasi pada setiap elemen dalam aliran. Niat reka bentuknya adalah

TimeStamp to Date in Java TimeStamp to Date in Java Aug 30, 2024 pm 04:28 PM

Panduan untuk TimeStamp to Date di Java. Di sini kita juga membincangkan pengenalan dan cara menukar cap waktu kepada tarikh dalam java bersama-sama dengan contoh.

Program Java untuk mencari kelantangan kapsul Program Java untuk mencari kelantangan kapsul Feb 07, 2025 am 11:37 AM

Kapsul adalah angka geometri tiga dimensi, terdiri daripada silinder dan hemisfera di kedua-dua hujungnya. Jumlah kapsul boleh dikira dengan menambahkan isipadu silinder dan jumlah hemisfera di kedua -dua hujungnya. Tutorial ini akan membincangkan cara mengira jumlah kapsul yang diberikan dalam Java menggunakan kaedah yang berbeza. Formula volum kapsul Formula untuk jumlah kapsul adalah seperti berikut: Kelantangan kapsul = isipadu isipadu silinder Dua jumlah hemisfera dalam, R: Radius hemisfera. H: Ketinggian silinder (tidak termasuk hemisfera). Contoh 1 masukkan Jejari = 5 unit Ketinggian = 10 unit Output Jilid = 1570.8 Unit padu menjelaskan Kirakan kelantangan menggunakan formula: Kelantangan = π × r2 × h (4

See all articles