集合的快速交集:C++ 与 C#

2023-11-27

在我的机器(四核,8GB RAM)上,运行 Vista x64 Business,使用 Visual Studio 2008 SP1,我试图非常快速地求出两组数字的交集。

我在 C++ 中实现了两种方法,在 C# 中实现了一种。到目前为止,C# 方法更快,我想改进 C++ 方法,使其比 C# 更快,我希望 C++ 可以做到。

以下是 C# 输出:(发布版本)

Found the intersection 1000 times, in 4741.407 ms

以下是两种不同方法的初始 C++ 输出(发布 x64 版本):

Found the intersection (using unordered_map) 1000 times, in 21580.7ms
Found the intersection (using set_intersection) 1000 times, in 22366.6ms

以下是三种方法的最新 C++ 输出(发布 x64 版本):

最新基准:

Found the intersection of 504 values (using unordered_map) 1000 times, in 28827.6ms
Found the intersection of 495 values (using set_intersection) 1000 times, in 9817.69ms
Found the intersection of 504 values (using unordered_set) 1000 times, in 24769.1ms

因此,set_intersection 方法现在比 C# 慢大约 2 倍,但比最初的 C++ 方法快 2 倍。

最新的C++代码:

Code:

// MapPerformance.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>
#include <unordered_set>

#include <boost\unordered\unordered_map.hpp>

#include "timer.h"

using namespace std;
using namespace stdext;
using namespace boost;
using namespace tr1;


int runIntersectionTest2(const vector<int>& set1, const vector<int>& set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_set<int> theSet;      

     theSet.insert( set1.begin(), set1.end() );

    int intersectionSize = 0;

    vector<int>::const_iterator set2_end = set2.end();

    for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
    {
        if ( theSet.find(*iterator) != theSet.end() )
        {
                intersectionSize++;
        }
    }

    return intersectionSize;
}

int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_map<int,int> theMap;  

    vector<int>::const_iterator set1_end = set1.end();

    // Now intersect the two sets by populating the map
    for ( vector<int>::const_iterator iterator = set1.begin(); iterator != set1_end; ++iterator )
    {
        int value = *iterator;

        theMap[value] = 1;
    }

    int intersectionSize = 0;

    vector<int>::const_iterator set2_end = set2.end();

    for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
    {
        int value = *iterator;

        unordered_map<int,int>::iterator foundValue = theMap.find(value);

        if ( foundValue != theMap.end() )
        {
            theMap[value] = 2;

            intersectionSize++;
        }
    }

    return intersectionSize;

}

int runSetIntersection(const vector<int>& set1_unsorted, const vector<int>& set2_unsorted)
{   
    // Create two vectors
    std::vector<int> set1(set1_unsorted.size());
    std::vector<int> set2(set2_unsorted.size());

    // Copy the unsorted data into them
    std::copy(set1_unsorted.begin(), set1_unsorted.end(), set1.begin());
    std::copy(set2_unsorted.begin(), set2_unsorted.end(), set2.begin());

    // Sort the data
    sort(set1.begin(),set1.end());
    sort(set2.begin(),set2.end());

    vector<int> intersection;
    intersection.reserve(1000);

    set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), back_inserter(intersection));

    return intersection.size(); 
}

void createSets( vector<int>& set1, vector<int>& set2 )
{
    srand ( time(NULL) );

    set1.reserve(100000);
    set2.reserve(1000);

    // Create 100,000 values for set1
    for ( int i = 0; i < 100000; i++ )
    {
        int value = 1000000000 + i;
        set1.push_back(value);
    }

    // Try to get half of our values intersecting
    float ratio = 200000.0f / RAND_MAX;


    // Create 1,000 values for set2
    for ( int i = 0; i < 1000; i++ )
    {
        int random = rand() * ratio + 1;

        int value = 1000000000 + random;
        set2.push_back(value);
    }

    // Make sure set1 is in random order (not sorted)
    random_shuffle(set1.begin(),set1.end());
}

int _tmain(int argc, _TCHAR* argv[])
{
    int intersectionSize = 0;

    vector<int> set1, set2; 
    createSets( set1, set2 );

    Timer timer;
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runIntersectionTest(set1, set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runSetIntersection(set1,set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runIntersectionTest2(set1,set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using unordered_set) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    getchar();

    return 0;
}

C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DictionaryPerformance
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> set1 = new List<int>(100000);
            List<int> set2 = new List<int>(1000);

            // Create 100,000 values for set1
            for (int i = 0; i < 100000; i++)
            {
                int value = 1000000000 + i;
                set1.Add(value);
            }

            Random random = new Random(DateTime.Now.Millisecond);

            // Create 1,000 values for set2
            for (int i = 0; i < 1000; i++)
            {
                int value = 1000000000 + (random.Next() % 200000 + 1);
                set2.Add(value);
            }

            long start = System.Diagnostics.Stopwatch.GetTimestamp();
            for (int i = 0; i < 1000; i++)
            {
                runIntersectionTest(set1,set2);
            }
            long duration = System.Diagnostics.Stopwatch.GetTimestamp() - start;

            Console.WriteLine(String.Format("Found the intersection 1000 times, in {0} ms", ((float) duration * 1000.0f) / System.Diagnostics.Stopwatch.Frequency));

            Console.ReadKey();
        }

        static int runIntersectionTest(List<int> set1, List<int> set2)
        {

            Dictionary<int,int> theMap = new Dictionary<int,int>(100000);

            // Now intersect the two sets by populating the map
            foreach( int value in set1 )
            {
                theMap[value] = 1;
            }

            int intersectionSize = 0;

            foreach ( int value in set2 )
            {
                int count;
                if ( theMap.TryGetValue(value, out count ) )
                {
                    theMap[value] = 2;
                    intersectionSize++;
                }
            }

            return intersectionSize;
        }
    }
}

C++代码:

// MapPerformance.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>

#include <boost\unordered\unordered_map.hpp>

#include "timer.h"

using namespace std;
using namespace stdext;
using namespace boost;

int runIntersectionTest(vector<int> set1, vector<int> set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_map<int,int> theMap;

    // Now intersect the two sets by populating the map
    for ( vector<int>::iterator iterator = set1.begin(); iterator != set1.end(); iterator++ )
    {
        int value = *iterator;

        theMap[value] = 1;
    }

    int intersectionSize = 0;

    for ( vector<int>::iterator iterator = set2.begin(); iterator != set2.end(); iterator++ )
    {
        int value = *iterator;

        unordered_map<int,int>::iterator foundValue = theMap.find(value);

        if ( foundValue != theMap.end() )
        {
            theMap[value] = 2;

            intersectionSize++;
        }
    }

    return intersectionSize;

}

int runSetIntersection(set<int> set1, set<int> set2)
{   
    set<int> intersection;

    set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.end()));

    return intersection.size(); 
}



int _tmain(int argc, _TCHAR* argv[])
{
    srand ( time(NULL) );

    vector<int> set1;
    vector<int> set2;

    set1.reserve(10000);
    set2.reserve(1000);

    // Create 100,000 values for set1
    for ( int i = 0; i < 100000; i++ )
    {
        int value = 1000000000 + i;
        set1.push_back(value);
    }

    // Create 1,000 values for set2
    for ( int i = 0; i < 1000; i++ )
    {
        int random = rand() % 200000 + 1;
        random *= 10;

        int value = 1000000000 + random;
        set2.push_back(value);
    }


    Timer timer;
    for ( int i = 0; i < 1000; i++ )
    {
        runIntersectionTest(set1, set2);
    }
    timer.Stop();

    cout << "Found the intersection (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    set<int> set21;
    set<int> set22;

    // Create 100,000 values for set1
    for ( int i = 0; i < 100000; i++ )
    {
        int value = 1000000000 + i;
        set21.insert(value);
    }

    // Create 1,000 values for set2
    for ( int i = 0; i < 1000; i++ )
    {
        int random = rand() % 200000 + 1;
        random *= 10;

        int value = 1000000000 + random;
        set22.insert(value);
    }

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        runSetIntersection(set21,set22);
    }
    timer.Stop();

    cout << "Found the intersection (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    getchar();

    return 0;
}

好的,这是最新的,有一些变化:

  • C++ 集现在已正确设置,因此它们具有 50% 的交集(如 C#)
  • Set1 已打乱,因此未排序,set2 已未排序
  • set_intersection 实现现在使用向量,并首先对它们进行排序

C++(发布版,x64)结果:

Found the intersection of 503 values (using unordered_map) 1000 times, in 35131.1ms
Found the intersection of 494 values (using set_intersection) 1000 times, in 10317ms

所以它比 C# 慢 2 倍。 @Jalf:你得到了一些非常快的数字,我在这里做错了什么吗?

C++代码:

// MapPerformance.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>

#include <boost\unordered\unordered_map.hpp>

#include "timer.h"

using namespace std;
using namespace stdext;
using namespace boost;

int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_map<int,int> theMap;  

    vector<int>::const_iterator set1_end = set1.end();

    // Now intersect the two sets by populating the map
    for ( vector<int>::const_iterator iterator = set1.begin(); iterator != set1_end; ++iterator )
    {
        int value = *iterator;

        theMap[value] = 1;
    }

    int intersectionSize = 0;

    vector<int>::const_iterator set2_end = set2.end();

    for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
    {
        int value = *iterator;

        unordered_map<int,int>::iterator foundValue = theMap.find(value);

        if ( foundValue != theMap.end() )
        {
            theMap[value] = 2;

            intersectionSize++;
        }
    }

    return intersectionSize;

}

int runSetIntersection(const vector<int> set1_unsorted, const vector<int> set2_unsorted)
{   
    // Create two vectors
    std::vector<int> set1(set1_unsorted.size());
    std::vector<int> set2(set2_unsorted.size());

    // Copy the unsorted data into them
    std::copy(set1_unsorted.begin(), set1_unsorted.end(), set1.begin());
    std::copy(set2_unsorted.begin(), set2_unsorted.end(), set2.begin());

    // Sort the data
    sort(set1.begin(),set1.end());
    sort(set2.begin(),set2.end());

    vector<int> intersection;
    intersection.reserve(1000);

    set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.end()));

    return intersection.size(); 
}

void createSets( vector<int>& set1, vector<int>& set2 )
{
    srand ( time(NULL) );

    set1.reserve(100000);
    set2.reserve(1000);

    // Create 100,000 values for set1
    for ( int i = 0; i < 100000; i++ )
    {
        int value = 1000000000 + i;
        set1.push_back(value);
    }

    // Try to get half of our values intersecting
    float ratio = 200000.0f / RAND_MAX;


    // Create 1,000 values for set2
    for ( int i = 0; i < 1000; i++ )
    {
        int random = rand() * ratio + 1;

        int value = 1000000000 + random;
        set2.push_back(value);
    }

    // Make sure set1 is in random order (not sorted)
    random_shuffle(set1.begin(),set1.end());
}

int _tmain(int argc, _TCHAR* argv[])
{
    int intersectionSize = 0;

    vector<int> set1, set2; 
    createSets( set1, set2 );

    Timer timer;
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runIntersectionTest(set1, set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runSetIntersection(set1,set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    getchar();

    return 0;
}

您的测试存在几个问题。

首先,您不是测试集合交集,而是“创建几个数组,用随机数填充它们,然后执行集合交集”。您应该只对您真正感兴趣的代码部分进行计时。即使您想要做这些事情,也不应该在这里对它们进行基准测试。一次测量一件事,以减少不确定性。如果您希望您的 C++ 实现性能更好,您首先需要知道它的哪一部分比预期慢。这意味着您必须将设置代码与交叉测试分开。

其次,您应该多次运行测试,以考虑可能的缓存效应和其他不确定因素。 (并且可能会输出 1000 次运行的总时间,而不是每次运行的单独时间。这样,您就可以减少计时器的不确定性,计时器的分辨率可能有限,并且在 0-20ms 范围内使用时会报告不准确的结果。

此外,据我从文档中读到,set_intersection 的输入应该进行排序,而 set2 不会。似乎没有理由使用unordered_map, when unordered_set会更适合你正在做的事情。

关于需要的设置代码,请注意您可能don't需要填充向量才能运行交集。您自己的实现和set_intersection已经在迭代器上工作了,所以你可以简单地将一对迭代器传递给你的输入已经存在的数据结构。

关于您的代码的一些更具体的评论:

  • Use ++iterator代替iterator++
  • 不要在每次循环迭代时调用 vector.end(),而是调用一次并缓存结果
  • 尝试使用排序向量 vs std::set vsunordered_set (not unordered_map)

Edit:

我还没有尝试过你的 C# 版本,所以我无法正确比较数字,但这是我修改后的测试。每个都在具有 4GB RAM 的 Core 2 Quad 2.5GHz 上运行 1000 次:

std::set_intersection on std::set: 2606ms
std::set_intersection on tr1::unordered_set: 1014ms
std::set_intersection on sorted vectors: 171ms
std::set_intersection on unsorted vectors: 10140ms

最后一个有点不公平,因为它必须对向量进行复制和排序。理想情况下,只有排序才应该成为基准的一部分。我尝试创建一个使用 1000 个未排序向量的数组的版本(因此我不必在每次迭代中复制未排序的数据),但性能大致相同,或者更差一些,因为这会导致持续的缓存丢失,所以我恢复到这个版本

我的代码:

#define _SECURE_SCL 0

#include <ctime>
#include <vector>
#include <set>
#include <iostream>
#include <algorithm>
#include <unordered_set>
#include <windows.h>

template <typename T, typename OutIter>
void stl_intersect(const T& set1, const T& set2, OutIter out){
    std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), out);
}

template <typename T, typename OutIter>
void sort_stl_intersect(T& set1, T& set2, OutIter out){
    std::sort(set1.begin(), set1.end());
    std::sort(set2.begin(), set2.end());
    std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), out);
}


template <typename T>
void init_sorted_vec(T first, T last){
    for ( T cur = first; cur != last; ++cur)
    {
        int i = cur - first;
        int value = 1000000000 + i;
        *cur = value;
    }
}

template <typename T>
void init_unsorted_vec(T first, T last){
    for ( T cur = first; cur != last; ++cur)
    {
        int i = rand() % 200000 + 1;
        i *= 10;

        int value = 1000000000 + i;
        *cur = value;
    }
}

struct resize_and_shuffle {
    resize_and_shuffle(int size) : size(size) {}

    void operator()(std::vector<int>& vec){
        vec.resize(size);

    }
    int size;
};

int main()
{
    srand ( time(NULL) );
    std::vector<int> out(100000);

    std::vector<int> sortedvec1(100000);
    std::vector<int> sortedvec2(1000);

    init_sorted_vec(sortedvec1.begin(), sortedvec1.end());
    init_unsorted_vec(sortedvec2.begin(), sortedvec2.end());
    std::sort(sortedvec2.begin(), sortedvec2.end());

    std::vector<int> unsortedvec1(sortedvec1.begin(), sortedvec1.end());
    std::vector<int> unsortedvec2(sortedvec2.begin(), sortedvec2.end());

    std::random_shuffle(unsortedvec1.begin(), unsortedvec1.end());
    std::random_shuffle(unsortedvec2.begin(), unsortedvec2.end());

    std::vector<int> vecs1[1000];
    std::vector<int> vecs2[1000];

    std::fill(vecs1, vecs1 + 1000, unsortedvec1);
    std::fill(vecs2, vecs2 + 1000, unsortedvec2);

    std::set<int> set1(sortedvec1.begin(), sortedvec1.end());
    std::set<int> set2(sortedvec2.begin(), sortedvec2.end());

    std::tr1::unordered_set<int> uset1(sortedvec1.begin(), sortedvec1.end());
    std::tr1::unordered_set<int> uset2(sortedvec2.begin(), sortedvec2.end());

    DWORD start, stop;
    DWORD delta[4];

    start = GetTickCount();
    for (int i = 0; i < 1000; ++i){
        stl_intersect(set1, set2, out.begin());
    }
    stop = GetTickCount();
    delta[0] = stop - start;

    start = GetTickCount();
    for (int i = 0; i < 1000; ++i){
        stl_intersect(uset1, uset2, out.begin());
    }
    stop = GetTickCount();
    delta[1] = stop - start;

    start = GetTickCount();
    for (int i = 0; i < 1000; ++i){
        stl_intersect(sortedvec1, sortedvec2, out.begin());
    }
    stop = GetTickCount();
    delta[2] = stop - start;

    start = GetTickCount();
    for (int i = 0; i < 1000; ++i){
        sort_stl_intersect(vecs1[i], vecs1[i], out.begin());
    }
    stop = GetTickCount();
    delta[3] = stop - start;

    std::cout << "std::set_intersection on std::set: " << delta[0] << "ms\n";
    std::cout << "std::set_intersection on tr1::unordered_set: " << delta[1] << "ms\n";
    std::cout << "std::set_intersection on sorted vectors: " << delta[2] << "ms\n";
    std::cout << "std::set_intersection on unsorted vectors: " << delta[3] << "ms\n";


    return 0;
}

C++ 没有理由总是比 C# 更快。 C# 有一些关键优势,需要非常小心才能与 C++ 竞争。 我能想到的第一个想法是,动态分配在 .NET 领域非常便宜。每次 C++ 向量、集合或 unordered_set(或任何其他容器)必须调整大小或扩展时,这是一个非常昂贵的过程malloc手术。在 .NET 中,堆分配只不过是向指针添加偏移量。

因此,如果您希望 C++ 版本竞争,您可能必须解决这个问题,允许您的容器调整大小而无需执行实际的堆分配,可能通过使用容器的自定义分配器(也许 boost::pool 可能是一个不错的选择)打赌,或者你可以尝试自己滚动)

另一个问题是set_difference仅适用于排序的输入,并且为了重现涉及排序的测试结果,我们必须在每次迭代中制作未排序数据的新副本,这是昂贵的(尽管同样,使用自定义分配器将有很大帮助)。我不知道您的输入采用什么形式,但您可以直接对输入进行排序,而无需复制它,然后运行set_difference直接在那个上。 (如果您的输入至少是一个数组或一个 STL 容器,那么这很容易做到。)

STL 的主要优点之一是它非常灵活,几乎可以处理任何输入序列。在 C# 中,您几乎必须将输入复制到列表或字典或其他内容,但在 C++ 中,您可能可以通过运行std::sort and set_intersection在原始输入上。

最后,当然,尝试通过分析器运行代码并准确查看时间花在哪里。您可能还想尝试通过 GCC 运行代码。我的印象是 MSVC 中的 STL 性能有时有点古怪。可能值得在另一个编译器下进行测试,看看是否有类似的计时。

最后,您可能会发现这些博客文章与 C++ 与 C# 的性能相关:http://blogs.msdn.com/ricom/archive/2005/05/10/416151.aspx

这些人的士气本质上是,是的,你可以在 C++ 中获得更好的性能,但这是一个令人惊讶的工作量。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

集合的快速交集:C++ 与 C# 的相关文章

随机推荐

  • C# 使用 xpath 解析 html

    我正在尝试使用 HTML 文档中的一段简单的 C 代码来解析股票交易信息 问题是我无法理解语法 tr 类 LomakeTaustaVari 被解析出来 但我如何获得没有 tr 类的第二位 这是 HTML 的一部分 它使用不同的值重复自身 t
  • HttpSession 为 SPRING_SECURITY_CONTEXT 返回 null 对象

    我正在尝试整合Spring Saml 库在示例 Web 应用程序中 使用 Shibboleth 作为 IDP 我能够加载登录页面 登录并显示索引页面 问题是 当我单击其他链接时 Web 应用程序会将我重定向到登录页面 然后 IDP 会识别我
  • 在 SwiftUI 中用波浪动画填充圆圈

    我在 swiftUI 中创建了一个圆圈 我想用正弦波动画填充它以实现水波效果 动画 我想用类似的外观来填充它 下面是我的代码 import SwiftUI struct CircleWaveView View var body some V
  • 删除每个 y 轴子图的第一个和最后一个刻度标签

    为了创建 5 个子图 我使用了 ax plt subplots 5 sharex True 然后 我想删除每个 y 轴子图的第一个和最后一个标签刻度 因为它们相互重叠 我使用了 plt setp a get yticklabels 0 1
  • 使用Sinon.js 存根类方法

    我正在尝试使用 sinon js 存根方法 但出现以下错误 Uncaught TypeError Attempted to wrap undefined property sample pressure as function 我也去了这个
  • python多处理池重试

    如果原始计算失败 是否可以使用简单的池重新发送一段数据进行处理 import random from multiprocessing import Pool def f x if random getrandbits 1 raise Val
  • 在Android Studio 4.0中安装CSV插件后无法打开项目设置和build.gradle

    这是我第一次在 Studio 4 0 中遇到这个问题 项目和应用程序级文件无法打开 下面是一些无法打开的其他文件 Android Studio 4 0 csv 文件未打开 不显示任何内容 java 文件未打开 kt 文件未打开 以前有其他人
  • 如何更改视图和部分视图的默认位置

    我是 MVC 新手 非常想知道如何更改视图和部分视图位置 我们知道视图和部分视图存储在视图文件夹中 如果我的控制器名称是 home 则视图必须存储在视图文件夹内的主文件夹中 并且所有部分视图存储在共享文件夹中 我想知道如何更改视图和部分视图
  • Chrome 要求使用 Thin 在我的 Rails 应用程序上为 SSL“选择证书”

    我有一个配置为仅使用 SSL 的 Rails 应用程序 我还拥有 StartSSL 提供的免费 SSL 证书 我通过以下命令使用 Thin 作为我的 Web 服务器 thin start p 80 thin start p 443 ssl
  • Snapchat 一次性下载所有记忆

    多年来 我在 Snapchat 上保存了很多照片 现在我想检索它们 问题是它们不容易导出 但幸运的是 如果您上网 您可以请求所有数据 这太棒了 我可以看到我的所有照片下载链接 并使用本地 HTML 文件 如果我单击 下载 它就会开始下载 这
  • 在 AFNetworking 中使用 URL 参数和 JSON 正文进行 POST

    我想进行一个同时包含 URL 参数和 JSON 正文的 POST 调用 URL http example com register apikey mykey JSON field value 如何通过 AFNNetworking 同时使用两
  • 用Python计算农历/阴阳假期

    这里有日历坚果吗 我一直在寻找有关如何计算公历中不规则发生的当年假期的信息 通常会发生这种情况 因为该假期是基于较旧的农历 我在谷歌上进行了令人作呕的搜索并取得了一些进展 但无法完成 如果有人有现代语言的示例代码来描述他们的计算 我将非常感
  • 在 for 循环中, (int i : high) 做什么,其中 high 是 int 数组 [重复]

    这个问题在这里已经有答案了 正如标题所说 有些人告诉我 如果我想打印数字数组中所有内容的总和 我应该使用上述参数进行 for 循环 如果需要进一步解释 代码将遵循 但其作用的确切定义是什么 我的意思是 部分 是吗 对于数组中的每个数字 i
  • )?图ql' aria-label='不知道如何转换Django字段技能()?图ql'> 不知道如何转换Django字段技能()?图ql

    我正在努力改变我的rest端点指向graphql我有一个图书馆叫TaggableManager作为其中之一model fields 有人知道这如何与 graphql 一起使用吗 提前致谢 你必须明确告诉石墨烯如何转换TaggableMang
  • 在 adb shell 中找不到 grep 命令

    我的手机通过adb连接到电脑后 进入adb shell 然后输入grep给我 grep not found 这是我的adb安装问题还是我的手机问题 如何让 grep 适用于我的设备 PS 这就是为什么我认为这可能是我的手机有问题的原因 而不
  • 错误:使用react-redux时,提供商的钩子调用无效

    我正在制作一个应用程序来从 url 获取参数并根据 url 的值工作 为了做到这一点 我尝试实现 redux 我想我编写了大部分代码 并且我是 React 和 Redux 的初学者 当我在 index js 文件中插入 Provider 时
  • 使用 Gmail 发送邮件的脚本:无法在 SMTP 服务器上进行身份验证

    我很难让 PHP 脚本使用 SMTP Gmail 帐户发送电子邮件 我正在使用 PHP 和 Symfony2 SwiftMailer 但问题似乎出在我的 Gmail 帐户配置上 在我的日志中 我最终收到以下错误消息 ERROR Except
  • 如何在 android ExoPlayer 中显示字幕

    我正在使用 Exoplayer 来播放 URL 我想在 exoplayer 中添加 srt 文件 但我认为播放器不支持srt文件 所以我将文件内容放入 1 个变量中 如何在 android exoplayer 中显示字幕 有点晚了 但它可能
  • GKE 集群升级到 1.14.6 后,VPN 无法访问内部网络

    我们将现有的开发集群从 1 13 6 gke 13 升级到 1 14 6 gke 13 我们的 Pod 无法再通过 Google Cloud VPN 访问我们的内部网络 我们的生产集群 仍在 1 13 上 共享相同的 VPC 网络和 VPN
  • 集合的快速交集:C++ 与 C#

    在我的机器 四核 8GB RAM 上 运行 Vista x64 Business 使用 Visual Studio 2008 SP1 我试图非常快速地求出两组数字的交集 我在 C 中实现了两种方法 在 C 中实现了一种 到目前为止 C 方法