抽象概念一定要被具象化才能被理解吗?

转载自 https://www.yueyao1982.com/phil_faq/faq_abstraction.html 若侵犯了内容创作者的权益,请联系删除 正文: 恰恰相反,具体的问题一定要抽象化之后才能理解,虽然这乍听起来比较奇怪。我们不妨想一想中学物理中最简单的问题,比如斜面上物体的运动。我们是先把物体和斜面都作了抽象化——物体和斜面都有很多细节,即“象”,而这些“象”被我们忽略了,或者说是抽离了,亦即“抽象”。所以,我们通过牛顿力学来理解机械运动,只是在理解抽象物体的相互关系,比如它们之间的作用,即力,比如它们之间的相对运动:简而言之,我们是在研究一个抽象模型,并试图理解这个模型。我们能做到最好的,就是我们的抽象化很合理,亦即被抽掉的“象”都是次要的;相应的,我们能得到最好的结果就是:模型的预测结果和实际的观测结果之间误差很小,一般来说不能没有误差,除非被观测值的可能性是离散甚至是有限的——比如电子的自旋只有两种可能。即使我们真的把主要因素抽掉,我们仍然可以理解那个被抽象出来的模型,只是那个模型和经验世界已经不能很好的对应了。 稍微深入一些来说,并非所有的抽象概念都能具象化,或者说概念的世界至少从规则和可能性这些方面来讲,并不受制于经验的世界,虽然没有经验概念就无从产生。比如我们观察过很多“经验中的马”,忽略了彼此不同的“象”,比如颜色、大小、公母等因素,就产生了“马的概念”;同样,我们观察过很多“经验世界中的鸟”,忽略了彼此不同的“象”,就产生了“鸟的概念”。“马的概念”和“鸟的概念”都可以被具象化到自然界中真实的动物。然而,我们的思维还可以对概念进行加工,比如我们对“鸟”概念进行切分,得到了“翅膀的概念”(当然它还是可以被具象化的)。然后我们把“翅膀的概念”和“马的概念”进行组合,得到了“天马”的概念——这个概念虽然可以具象化为具体的玩具或模型,但不再能具象化为自然界中的动物了。与此相似,我们还可以进行更复杂的概念建构,比如用更多的翅膀构建出“六翼天使”,比如用多种动物的不同部分构建出了“龙”和“麒麟”等神兽。当然,到此为止,这些概念至少还是可以被在一定程度上具象化,比如画成图画,比如做成雕刻。但是,更进一步的概念建构使这点也不可能了。如果我们把自然数的计数泛化到空间的维度上,我们就构建出了N维空间——1维空间、2维空间和3维空间是可以被具象化的,4维...

leetcode——454. 4SumII题解

 题目要求:

    Given four integer arrays nums1, nums2, nums3, and nums4 all of length n, return the number of tuples (i, j, k, l) such that:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

 

Example 1:

Input: nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
Output: 2
Explanation:
The two tuples are:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

Example 2:

Input: nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
Output: 1

解法:

    
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> record;
        for(int i = 0; i < nums3.size(); i++){
            for(int j = 0; j < nums4.size(); j++){
                record[nums3[i] + nums4[j]] ++;
            }
        }
        int res = 0;
        for(int i = 0; i < nums1.size(); i++){
            for(int j = 0; j < nums2.size(); j++){
                if(record.find(0 - nums1[i] - nums2[j]) != record.end()){
                    res += record[0 - nums1[i] - nums2[j]];
                }
            }
        }
        return res;
    }
};

这段代码也通过先计算出nums3与nums4中各元素的和,以减小时间复杂度。

    时间复杂度:O(N^2)

    空间复杂度:O(N^2)(开辟了新的nums3*nums4大小的数组以存放nums3与nums4数组中各个元素的和。故空间复杂度为O()N^2

    边界问题:

    由于是在4个不同的数组中计算,其边界问题比较简单(就只有【0, size - 1】)如果是在单个数组中进行计算,其边界问题就更为复杂(比如leedcode中的第18题)。

    是否有改进的地方:

    可能存在时间复杂度为O(N)的解法,即将nums[2], nums[3], nums[4]的各个元素之和都放入查找表中。

    应该没有比时间复杂度为O(N^2)更低的解法了,如果是将nums[2], nums[3], nums[4]都放入查找表中,其放入的代码的时间复杂度就有O(N^3)了,所以实际上将nums[2], nums[3], nums[4]都放入查找表中是增加了其时间复杂度和空间复杂度,得不偿失。

    


    是否有其他解法:

    解法当然是有的,而且还很多。但是时间复杂度都比较大
    比如最暴力的四层循环时间复杂度为O(N^4);unordered_map的寻找单个数字,其时间复杂度为O(N^3)(unordered_map的底层实现是哈希表,其查找的时间复杂度是O(1)) 。
    

此博客中的热门博文

算法的八大常用思想